ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ObjectiveSQL Tutorial

Braisdom edited this page Dec 6, 2020 · 3 revisions

1 Preface

ObjectiveSQL is an ORM framework, which is not only the best practice of "Active Record" pattern in Java, but also provides a perfect solution for complex SQL programming. It makes Java codes and SQL statements organic combination, changes the traditional SQL programming model (strings programming model).

The Objective-SQL project contains two parts: one is the dependency of maven objective-sql or objsql-springboot , with the basic ORM features and SQL programming model, and the other is the IntelliJ IDEA plug-in, compatible with Java operator overloading and code completion.

Features:

  • Dynamical codes generations: with one annotation your Class has a fully featured SQL programming capability
  • Java API of SQL: abstracting and modeling the elements of the SQL language, integrating SQL and Java. Easy to encapsulating, programmed and unit testing
  • Expression syntax consistency: Java syntax and SQL syntax equivalent replacement, including: arithmetic, function calls, compared with the logic calculation expression

2 Dependencies installation

2.1 IntelliJ IDEA plug-in installation

Preferences/Settings -> Plugins -> Search with "ObjectiveSql" in market -> Install

2.2 Maven integration

<!-- In standalone -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>{objsql.version}</version>
</dependency>
<!-- In Spring Boot, you need add spring-jdbc dependency before -->
<dependency>
<groupId>com.github.braisdom</groupId>
<artifactId>objsql-springboot</artifactId>
<version>{objsql.version}</version>
</dependency>

2.3 Maven compiler configration

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-Xplugin:JavaOO</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github.braisdom</groupId>
<artifactId>objective-sql</artifactId>
<version>${objsql.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

The latest versions of the ObjectiveSQL and ObjSqlSpringBoot can be found in Maven Central.

3 Database connection injection

3.1 In standalone

Injecting the ConnectionFactory into the ObjectiveSQL, based on MySQL

privatestaticclassMySQLConnectionFactoryimplementsConnectionFactory {
@OverridepublicConnectiongetConnection(StringdataSourceName) throwsSQLException {
try {
Stringurl = "jdbc:mysql://localhost:4406/objective_sql";
Stringuser = "root";
Stringpassword = "******";
returnDriverManager.getConnection(url, user, password);
} catch (SQLExceptione) {
throwe;
} catch (Exceptione) {
thrownewIllegalStateException(e.getMessage(), e);
}
}
}
Databases.installConnectionFactory(new MySQLConnectionFactory());

The dataSourceName of method getConnecition is for multiple data sources, you can ignore it if one data source. You don't forget to inject it into Databases.

3.2 In Spring Boot

spring:
profiles:
name: objective-sql-exampleactive: developmentdatasource:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Master# Configurations for multiple databasesextensions:
# The name of data source, which will match with @DomainModel definition slave:
driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:4406/objective_sqlusername: rootpassword: ******hikari:
idle-timeout: 10000maximum-pool-size: 10minimum-idle: 5pool-name: Slave

The tag extensions is used in multiple data sources, and the tag slave is name of other data source, which is match with definition in domain model

4 Simple SQL programming guide

4.1 Definition of domain model

@DomainModelpublicclassMember {
@Size(min = 5, max = 20)
privateStringno;
@QueryableprivateStringname;
privateIntegergender;
privateStringmobile;
@TransientprivateStringotherInfo;
@Relation(relationType = RelationType.HAS_MANY)
privateList<Order> orders;
}

4.2 Querying

Member.countAll();
Member.count("name = ?", "braisdom");
Member.queryByPrimaryKey(1);
Member.queryFirst("id > ?", 1);
Member.query("id > ?", 1);
Member.queryAll();

4.3 Persistence

Member.create(newMember);
Member.create(newMember, true); // Create a member without validatingMember.create(Member.newInstanceFrom(memberHash));
Member.create(newMember[]{newMember1, newMember2, newMember3}, false);
Member.update(1L, newMember, true); // Update a member by primary key and skip validationgMember.update("name = ?", "name = ?", newName, oldName);
Member.destroy(1L); // Delete a member by primary keyMember.destroy("name = ?", "Mary");

4.4 Transaction

4.4.1 Transaction based on annotation
// The method will be executed in a database thransaction@TransactionalpublicstaticvoidmakeOrder(Orderorder, OrderLine... orderLines) throwsSQLException {
Order.create(order, false);
OrderLine.create(orderLines, false);
}
4.4.2 Transaction manually
// Transaction executing manuallyDatabases.executeTransactionally(((connection, sqlExecutor) -> {
Member.update(1L, newMember, true);
Member.update("name = ?", "name = ?", newName, oldName);
returnnull;
}));

4.5 Relation querying

Member.queryAll(Member.HAS_MANY_ORDERS);
Member.queryFirst("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.query("id > ?", Member.HAS_MANY_ORDERS, 1);
Member.queryByPrimaryKey(1, Member.HAS_MANY_ORDERS);
Member.queryByName("braisdom", Member.HAS_MANY_ORDERS);

4.6 Paged querying

// Create a Page instance with current page and page sizePagepage = Page.create(0, 10);
PagedList<Member> members = Member.pagedQueryAll(page, Member.HAS_MANY_ORDERS);
PagedList<Member> members = Member.pagedQuery(page, "name = ?", "braisdom");

4.7 Customized query with Query interface

Queryquery = Member.createQuery();
query.project("name").groupBy("name").having("COUNT(*) > 0").orderBy("name DESC");
List<Member> members = query.execute(Member.HAS_MANY_ORDERS);
// Paged querying with querying dynamicallyPaginatorpaginator = Databases.getPaginator();
Pagepage = Page.create(0, 10);
PagedList<Member> pagedMembers = paginator
.paginate(page, query, Member.class, Member.HAS_MANY_ORDERS);

4.8 Validation

The Jakarta Bean Validation is integrated into ObjectiveSQL

See more:https://beanvalidation.org/

4.8.1 validate manually
MembernewMember = newMember()
.setNo("100")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
// Violations occurred in field 'no'Validator.Violation[] violations = newMember.validate();
4.8.2 validate on object creating
MembernewMember = newMember()
.setNo("100000")
.setName("Pamela")
.setGender(1)
.setMobile("15011112222");
Member.create(newMember); Member.create(newMember, true); // Skip validation

4.9 Customized SQL querying

Member.execute("DELETE FROM members WHERE name = ?", "Pamela");

5 Complex SQL programming guide

5.1 JOIN

5.1.1 Implicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id` )
GROUP BY`T0`.`NO` , `T0`.`name`
5.1.2 Explicit join
Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member)
.leftOuterJoin(order, order.memberId.eq(member.id))
.groupBy(member.no, member.name);
List<Member> members = select.execute(Member.class);
SELECT`T0`.`NO` , `T0`.`name` , COUNT(*) AS`order_count`FROM`members`AS`T0`LEFT OUTER JOIN`orders`AS`T1`ON (`T1`.`member_id`=`T0`.`id` )
GROUP BY`T0`.`NO` , `T0`.`name`

5.2 Paged querying

Member.Tablemember = Member.asTable();
Order.Tableorder = Order.asTable();
Paginator<Member> paginator = Databases.getPaginator();
Pagepage = Page.create(0, 20);
Selectselect = newSelect();
select.project(member.no, member.name, count().as("order_count"))
.from(member, order)
.where(member.id.eq(order.memberId))
.groupBy(member.no, member.name);
PagedList<Member> members = paginator.paginate(page, select, Member.class);
-- Counting SQLSELECTCOUNT(*) AS count_
FROM (
SELECT`T0`.`NO`,
`T0`.`name`,
COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`
) T
-- Querying SQLSELECT`T0`.`NO`, `T0`.`name`, COUNT(*) AS`order_count`FROM`members`AS`T0`, `orders`AS`T1`WHERE (`T0`.`id`=`T1`.`member_id`)
GROUP BY`T0`.`NO`, `T0`.`name`LIMIT0, 20

5.3 Querying with complex expression

Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(orderTable.quantity > 30 &&
orderTable.salesAt.between("2020-05-01 00:00:00", "2020-05-02 23:59:59"))
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((`T0`.`quantity`>30)
AND`T0`.`sales_at` BETWEEN '2020-05-01 00:00:00'AND'2020-05-02 23:59:59' )
GROUP BY`T0`.`member_id`

5.4 Querying with dynamic arguments

String[] filteredNo = {"202000001", "202000002", "202000003"};
intfilteredQuantity = 0;
Order.TableorderTable = Order.asTable();
Selectselect = newSelect();
LogicalExpressioneternalExpression = newEternalExpression();
if(filteredNo.length > 0) {
eternalExpression = eternalExpression.and(orderTable.no.in(filteredNo));
}
if(filteredQuantity != 0) {
eternalExpression = eternalExpression.and(orderTable > filteredQuantity);
}
select.project((sum(orderTable.amount) / sum(orderTable.quantity) * 100).as("unit_amount"))
.from(orderTable)
.where(eternalExpression)
.groupBy(orderTable.memberId);
List<Order> orders = select.execute(Order.class);
SELECT ((((SUM(`T0`.`amount` ) /SUM(`T0`.`quantity` ) )) *100)) AS unit_amount
FROM`orders`AS`T0`WHERE ((1=1) AND`T0`.`NO`IN ('202000001', '202000002', '202000003') )
GROUP BY`T0`.`member_id`

6 Advanced usage

6.1 LoggerFactory injection

6.1.1 LoggerFactory implementation
publicclassObjLoggerFactoryImplimplementsLoggerFactory {
privateclassObjLoggerImplimplementsLogger {
privatefinalorg.slf4j.Loggerlogger;
publicObjLoggerImpl(org.slf4j.Loggerlogger) {
this.logger = logger;
}
@Overridepublicvoiddebug(longelapsedTime, Stringsql, Object[] params) {
logger.debug(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoidinfo(longelapsedTime, Stringsql, Object[] params) {
logger.info(createLogContent(elapsedTime, sql, params));
}
@Overridepublicvoiderror(Stringmessage, Throwablethrowable) {
logger.error(message, throwable);
}
privateStringcreateLogContent(longelapsedTime, Stringsql, Object[] params) {
String[] paramStrings = Arrays.stream(params)
.map(param -> String.valueOf(param)).toArray(String[]::new);
StringparamString = String.join(",", paramStrings);
returnString.format("[%dms] %s, with: [%s]",
elapsedTime, sql, String.join(",",
paramString.length() > 100 ? StringUtil
.truncate(paramString, 99) : paramString));
}
}
@OverridepublicLoggercreate(Class<?> clazz) {
org.slf4j.Loggerlogger = org.slf4j.LoggerFactory.getLogger(clazz);
returnnewObjLoggerImpl(logger);
}
}
6.1.1 Injecting in standalone
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
// others
}
}
6.1.2 Injecting Spring Boot
@SpringBootApplication@EnableAutoConfigurationpublicclassApplication {
publicstaticvoidmain(String[] args) {
SpringApplicationspringApplication = newSpringApplication(Application.class);
springApplication.addListeners(newApplicationListener<ApplicationReadyEvent>() {
@OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent) {
Databases.installLoggerFactory(newObjLoggerFactoryImpl());
}
});
springApplication.run(args);
}
}

6.2 Cache objects with SQL

6.1.1 SQLExecutor implementation
publicclassCacheableSQLExecutor<T> extendsDefaultSQLExecutor<T> {
privatestaticfinalList<Class<? extendsSerializable>> CACHEABLE_CLASSES =
Arrays.asList(newClass[]{Member.class});
privatestaticfinalIntegerCACHED_OBJECT_EXPIRED = 60;
privatestaticfinalStringKEY_SHA = "SHA";
privateJedisjedis = newJedis("localhost", 6379);
privateMessageDigestmessageDigest;
publicCacheableSQLExecutor() {
try {
messageDigest = MessageDigest.getInstance(KEY_SHA);
} catch (NoSuchAlgorithmExceptione) {
thrownewIllegalArgumentException(e.getMessage(), e);
}
}
@OverridepublicList<T> query(Connectionconnection, Stringsql,
TableRowAdaptertableRowAdapter, Object... params) throwsSQLException {
Class<?> domainClass = tableRowAdapter.getDomainModelClass();
if (CACHEABLE_CLASSES.contains(domainClass)) {
if(!Serializable.class.isAssignableFrom(domainClass)) {
thrownewIllegalArgumentException(String
.format("The %s cannot be serialized"));
}
messageDigest.update(sql.getBytes());
StringhashedSqlId = newBigInteger(messageDigest.digest()).toString(64);
byte[] rawObjects = jedis.get(hashedSqlId.getBytes());
if (rawObjects != null) {
return (List<T>) SerializationUtils.deserialize(rawObjects);
} else {
List<T> objects = super.query(connection, sql, tableRowAdapter, params);
byte[] encodedObjects = SerializationUtils.serialize(objects);
SetParamsexpiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);
jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);
returnobjects;
}
}
returnsuper.query(connection, sql, tableRowAdapter, params);
}
}
6.1.1 Injecting
publicclassApplication {
publicstaticvoidmain(String[] args) {
Databases.installSqlExecutor(newCacheableSQLExecutor());
// others
}
}

6.3 ColumnTransition extension

publicclassSqlDateTimeTransition<T> implementsColumnTransition<T> {
@OverridepublicObjectsinking(DatabaseMetaDatadatabaseMetaData, Tobject,
TableRowAdaptertableRowDescriptor, StringfieldName, FieldValuefieldValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
if (fieldValue != null && fieldValue.getValue() != null) {
if (SQLite.equals(databaseName) || Oracle.equals(databaseName)) {
returnfieldValue;
} elseif (PostgreSQL.equals(databaseName)) {
if (fieldValue.getValue() instanceofTimestamp) {
returnfieldValue.getValue();
} elseif (fieldValue.getValue() instanceofLong) {
Instantvalue = Instant.ofEpochMilli((Long) fieldValue.getValue());
returnTimestamp.from(value);
} else {
returnTimestamp.valueOf(String.valueOf(fieldValue.getValue()));
}
} else {
returnfieldValue;
}
}
returnnull;
}
@OverridepublicObjectrising(DatabaseMetaDatadatabaseMetaData, ResultSetMetaDataresultSetMetaData,
Tobject, TableRowAdaptertableRowDescriptor, StringcolumnName, ObjectcolumnValue) throwsSQLException {
StringdatabaseName = databaseMetaData.getDatabaseProductName();
try {
if (columnValue != null) {
if (SQLite.equals(databaseName)) {
Instantvalue = Instant
.ofEpochMilli(Long.valueOf(String.valueOf(columnValue)))
returnTimestamp.from(value);
} else {
returncolumnValue;
}
}
} catch (DateTimeParseExceptionex) {
Stringmessage = String.format("Invalid raw DataTime of '%s' from database: %s",
columnName, columnValue);
thrownewIllegalArgumentException(message, ex);
}
returnnull;
}
}