Technologies for development of data business logic in Java: Hibernate and SPL

 

To achieve the business logic focused on database in Java, three key elements are required: the objectification of the database table, the ability to calculate the structured data, and the ability to process the flow. Hibernate is an old and well-established technology, and has long since provided the three elements, and achieved a large number of data business logics in many projects. SPL is a new data computing language. Likewise, SPL has the said elements, which can also be used to achieve data business logic. This article will compare them in many aspects so as to find out the one with more efficiency in developing the data business logic. As for other related technologies (such as MyBatis, Querydsl, JOOQ), they are not discussed in this article for reasons like code coupling, computing capability and maturity.

Basic features

Programming style

Hibernate is provided with two programming styles that differ greatly: one belongs to the object-oriented programming, which uses Java's EntityBean and if/switch/while/for statements to process the structured data objects and the flow; the other uses HQL that is close to the programming style of SQL to compute the structured data. In contrast, SPL greatly simplifies the object orientation, which has the concept of objects, and can use the dot to access attributes and perform multi-step calculation. However, SPL cannot be regarded as a complete object-oriented language for it does not have related contents like inheritance and overloading.

Operation mode

Hibernate has two operating modes that are quite different: Java code belongs to compiled execution, and HQL belongs to interpreted execution. SPL is an interpreted language, and more flexible in coding, yet the performance is slightly worse for the same code. However, since SPL has rich and efficient library functions, the overall performance is not inferior, and it is often more advantageous while processing big data.

External library

Hibernate can introduce any other Java library of third-party to make up for its shortcomings, such as using Mybatis to improve short-term development efficiency, and using Stream to make up for the computing capability of HQL. SPL has built-in professional data processing functions, providing a large number of basic operations boasting higher development efficiency and lower time complexity. Usually, there is no need for SPL to use external Java libraries, which can be called in self-defined functions in special cases.

IDE and debugging

Both Hibernate and SPL have the graphical IDE and complete debugging function. The IDE of SPL is specially designed for data processing, and the structured data objects are presented in the form of table, and hence it is easier to observe. In contrast, Hibernate uses Java IDE. Although this IDE is more general, it is inconvenient to observe structured data objects as a result of not being optimized for data processing.

Learning difficulty

To learn Hibernate, there is a need to learn two languages: Java, and then HQL. Since there is a big difference between the two languages, and it needs to use them in a combined way, the learning difficulty is thus increased. In addition, HQL tries to simplify SQL with object-oriented concept, yet some operations (such as association, sub-query) are something that SQL is good at but are difficult to express with object. In order to implement such operations, HQL designs more complex data structure and syntax rule than SQL, therefore, it is actually more difficult to learn HQL than SQL. The objective of SPL is to simplify the code of Java and even SQL, and everything it does is for data computing. SPL intentionally simplifies many concepts; the difficulty of learning SPL is therefore very low.

Amount of code

The expression ability of HQL is not as good as SQL, it needs to implement complex structured data calculation in an indirect way by using special means including the hard coding, dialect SQL, high-performance computing library, etc., which makes it very difficult to code, integrate and manage. Moreover, Java's flow processing statement is designed for general-purpose computing, not specifically designed for structured data object (List<EntityBean>), and the amount of code is thus not small. SPL has stronger expression ability than SQL, and can implement structured data calculation with lower code amount. Besides, SPL's flow processing statements are specially designed for structured data objects, and the code amount is lower than that of Java.

Structured data objects

Structured data objects are used to objectify the database table, which is the basis for data processing and business logic development. A professional structured data object can easily exchange the data with database, supports rich calculation functions, and simplifies the difficulty of flow processing.

Unfortunately, Hibernate does not have the professional structured data object. To solve this problem, it generally uses Java's List<EntityBean> (this article will focus on it to illustrate), and sometimes uses Stream<EntityBean> or List<Object[]>. On the contrary, SPL provides professional structured data objects, including the table sequence (this article will focus on it to illustrate), internal table compression table, external storage Lazy cursor, etc.

Definition

Hibernate (Java) is a strongly typed compiled language, this language must define the data structure before using structured data objects, which is quite troublesome. Let's take the order table as an example, first define the entity configuration file name and entity class name in hibernate.cfg.xml:

<mapping resource="OrdersEntity.hbm.xml"/>
<mapping class="table.OrdersEntity"/>

Then, define the metadata correspondence between database table and entity class in the entity configuration file. Part of code:

<class name="table.OrdersEntity" table="orders" schema="test">
<id name="orderId" column="OrderID"/>
<property name="client" column="Client"/>
<property name="sellerId" column="SellerId"/>
<property name="amount" column="Amount"/>
<property name="orderDate" column="OrderDate"/>
</class>

Then, implement the specific attributes and methods in the entity class, part of code:

@Entity
@Table(name = "orders", schema = "test", catalog = "")
public class OrdersEntity {
private Integer orderId;
private String client;
private Integer sellerId;
private Double amount;
private Date orderDate;
@Basic
@Column(name = "OrderID")
public Integer getOrderId() {
return orderId;
}
…

The above process is the simplest definition process. If using Hibernate to deal with more complicated situations like multi-table association, auto-increment type, and joint primary key, it will be more troublesome to define. For example, for the joint primary key, the definition of extra classes is required. In fact, the process of defining structured data objects in Hibernate is so complicated than SQL that it needs to use special annotations to simplify the process. Even so, it does not achieve the objective of improving development efficiency. Although tools can be used to automate the definition process, it is always difficult to avoid manual definition (example will be given later).

SPL is an interpreted language, and can easily infer a new data structure after data fetching and computing, and there is no need to define the data structure in advance in general.

Read data from database

Reading the records (set) from the external database as the List<EntityBean> inside the Hiberante is mainly performed with the approaches: primary key query, HQL query, SQL query, etc. Let's take the most common HQL query as an example:

String hql ="from OrdersEntity where sellerId=10";
Query query = session.createQuery(hql);
List<OrdersEntity> orders = query.list();
for(OrdersEntity order : orders){
System.out.println(order.getOrderId()+"\t"+order.getClient()+"\t"+order.getSellerId()+"\t"+order.getAmount()+"\t"+order.getOrderDate());
}

This Hibernate code is not long and its logic is easy to understand. It should be noted that the data structure must be defined first, and the subsequent output code is not a must, mainly for solving the trouble that it is inconvenient to observe the results in IDE.

SPL generates the table sequence mainly through SQL query:

T=orcl.query("select * from test.Orders where sellerId=?",10)

We can see that SPL code is simpler, for there is no need to define the data structure in advance, no additional output is required, and the results can be viewed intuitively in IDE.

Write data to database

For Hibernate, persisting the processed structured data objects and saving to the database are achieved mainly through the functions such as save (adding an entity), update (modifying an entity), and delete (deleting an entity). Let’s take the batch adding of entities as an example:

Transaction tx = session.beginTransaction();
for ( int i=0; i<orders.size(); i++ ) {
session.save(orders.get(i));
}
tx.commit();

Hibernate code does not seem to be troublesome, but there is hidden trouble in fact. Hibernate will continuously generate the buffers when saving/updating entities until the memory crashes or the buffers are manually emptied, which makes the above code only suitable for adding/modifying a small number of entities. If more entities are added/modified, the out-of-memory problem caused by buffers must be solved, and the above code needs to be modified in a way that refresh the buffers to database every time a certain number of entities are added (such as 100 pieces, depending on memory capacity and concurrency number), and then empty the buffers (session. Clear()). Hibernate implements the addition, modification and deletion of records respectively with different functions. Since these functions are quite different in usage, it needs to divide into three cases for batch processing if the records need to be added, modified and deleted at the same time, and the code will be much more complicated.

SPL implements the addition, modification and deletion of a single or batch records only with one update function. Moreover, SPL supports updating the records in a mixed way. For example, the original table sequence is T, and the table sequence after a series of additions, deletions and modifications is NT, the following code is to persist the changed results to the order table of the database:

orcl.update(NT:T,orders)
orcl.commit()

As can be seen that the SPL code is much simpler, and there is no need to manually control the buffers or memory.

Access the field

Hibernate uses EntityBean as the structured data object, and can easily access the field in the form of object. For example, the following code is to read the Client field of a single record:

Orders.get(3).getClient()

Obviously, it is convenient for Hibernate to access the field of a single record, but once you want to access the field of record set, it is not easy to code. For example, the following code is to get a column of records:

List<String> Clients=Orders.stream().map(Order->Order.getClient()).collect(Collectors.toList());

In order to simplify the code, the Stream object is used in this code, which is supported in Java8 or higher version, otherwise the coding will be more complicated. If you use Java16 or higher version, it will be easier to convert back to List finally, but it is still troublesome on the whole. As for getting multiple fields, getting by the default field name, by string name, or by field’s sequence number, it will be more troublesome for Hibernate to implement. Even for the seemingly basic operation of getting the list of field names, it is quite troublesome for Hibernate. All of the above troubles indicate that Hibernate is not very professional.

The table sequence of SPL is a professional structured data object, and the field can also be accessed easily in the form of object. For example: to read the Client field of a single record:

Orders(3).Client

SPL’s professional ability is embodied more in accessing the field of record set, such as getting a column of records:

Orders.(Client)

What the more professional ability of SPL is that it provides a variety of field accessing approaches to improve the development efficiency in different scenarios:

Orders.([Client,Amount])		//Get multiple fields
Orders.([#2,#3])			//Get through default field name
Orders.field(“Client”)		        //Get through string name (external parameter)
Orders.field(2)			        //Get through field sequence number 
Orders.fname()				//Get a list of field names

Ordered access

Hiberante's List<EntityBean> is an ordered set, supporting order-related functions. However, due to lack of professional ability, only the most basic functions are provided. For example, the following codes are to fetch the records by index and interval respectively:

Orders.get(3)
Orders.subList(3,5); 

For further functions, it has to implement through hard coding. For example, to fetch the last N records, the code is:

Collections.reverse(o1);
List<OrdersEntity> o4= o1.subList(0,3);

As for the functions such as fetching records by position set or by step-by-step, it is more troublesome for Hiberante to express.

SPL’s table sequence is also an ordered set, providing the basic functions related to order, such as fetching the records by index and interval:

Orders(3)
Orders.to(3,5)

Table sequence is a professional structured data object. SPL directly provides many order-related advanced functions that Hibernate does not support, such as fetching the records by sequence number that counts backwards can be directly represented by a negative sign:

Orders.m(-3)				//The 3rd record from last
Orders.m(to(-3,-5))			//Get records by interval that counts backwards

For another example, fetch the records by position set and by step-by-step:

Orders.m(1,3,5,7:10)			//Records with sequence number 1, 3, 5, 7-10
Orders.m(-1,-3,-5)			//The 1st, 3rd and 5th records from last
Orders.step(2,1)			//The first of every 2 records (equivalent to fetching by odd position)

Structured data calculation

The ability to calculate the structured data is the core element of data business logic. Here below are three common calculations we selected in the order from easy to difficult to compare the computing codes of Hibernate and SPL.

Basic calculations

Basic calculations include: selecting some of fields, filtering, grouping and aggregating, sorting, association, etc. Now we take the simplest calculation as an example: select some fields from the existing structured data objects to form a new structured data object. The following code is written in SQL:

select client,amount from Orders

Hibernate code:

String hql = "select new ClientAmount(client,amount) from Orders";
Query query = session.createQuery(hql);
List<ClientAmount> clientAmounts = query.list();

In terms of computing code alone, Hibernate is not much more complex than SQL, the real trouble is that it needs to manually define a new entity class (ClientAmount) in advance, and this entity class should contain the selected fields, and such fields should be reflected in the parameter of the constructor. Defining the result before coding calculation process violates normal thinking habit and seriously affects the fluency of business logic development.

In addition to the conventional method described above, there are two other methods. One is to add a new constructor in existing entity class, this methods eliminates the definition of the new class, but increases the coupling between the calculation codes, which make its disadvantages outweigh the advantages; the other is to return the List<Object[]> directly with HQL"select id,name from Orders”. For this method, there is no need to define new class, but it will lead to the inconsistency of structured data types and bring more troubles.

Defining the data structure in advance is not limited to the operation “selecting some of fields”, other operations also need to define the data structure in advance such as grouping and aggregating, association. Among them, it is particularly troublesome to define the result of the association calculation in advance. For more complicated calculations, multiple basic calculations need to be combined, you can imagine how troublesome the implementation process will be. These troubles reflect Hibernate’s lack of professional ability in the development of data business logic. This lack is essentially because that HQL is an interpreted code, while EntityBean is a compiled code. Although the two codes are barely combined in Java, their styles are quite different, and the interface is too complicated, making them work together inconveniently.

For the same calculation task, SPL code is much simpler:

Orders.new(Client,Name)

SPL does not need to define the data structure of calculation result in advance. Similarly, SPL does not need to define the data structure for any basic or complex calculation. SPL's simple and easy-to-understand calculation code reflects its professional ability in the development of data business logic. The essential reason is that SPL is an interpreted language, and its table sequence and calculation functions are designed as a whole, making the code style consistent, the driver simple, and the combined use conveniently.

Multi-step calculation

Continuing to query based on the previous query result can divide a big calculation target into multiple small steps. In this way, a complex problem can be finally solved by solving simple problems step by step. Multi-step calculation in SQL is the from or with subquery, such as:

select orderId, m from (select orderId, month(orderDate) m from Orders) t1

Unfortunately, Hibernate does not directly support multi-step calculation, it has to use an indirect method. A common method is to use the above dialect SQL, which is not repeated here.

Indeed, the dialect SQL can solve the problem in the short term, but it seriously violates the original intention of Hibernate, and loses the advantages of object-oriented, (Java) consistent style, and migration, etc. In addition to the dialect SQL, other methods can also be used. Common methods include the Java hard coding, SQL view, Hibernate virtual views, etc., yet no matter which method is used, the development efficiency will not be too high, and the maintenance will be very troublesome in the future.

SPL code:

Orders.new(orderId, month(orderDate): m).new(orderId, m)

Multi-step calculation is the basic function of SPL, and it is easy for SPL to achieve. Moreover, SPL has the advantages of object access, consistent style, and strong migration.

Ordered operation

Let's start with simple example: to get the first N records, SQL (Oracle) code:

select orderId,client,amount,orderDate from OrdersEntity where rownum<=3

Hibernate code:

session.createQuery("select orderId,client,amount,orderDate from OrdersEntity").setMaxResults(3);

Hibernate HQL doesn’t support rownum, and cannot implement the calculation task independently, therefore, it needs to use Java code (setMaxResults) to solve this problem. Although the above code is not long, it needs to switch between two totally different syntaxes, which will bring you a poor coding experience. In addition, it should be noted that EntityBean is ordered, while HQL is unordered. Since there is a big difference between them, it is difficult to combine them. Yet, they are forced to work together to implement the calculation task, this is also a reflection of lacking the professional ability in Hibernate.

SPL code:

Orders.new(orderId,client,amount,orderDate).to(3)

As you can see that SPL code is simpler, and uses only one code style. There is no separation sense and switch action when coding in SPL, the coding experience is thus better.

Let's look at a slightlymore complicated example: to group and calculate the LRR, corresponding SQL (Oracle) code can use the window function to implement in general:

select sales,month,v1,v2, (v1-v2)/v2 looprate from(
   select sales, month,amount v1,lag(amount,1) over(partition by sales order by month)  
   v2 from tbl)

HQL is not very professional, and does not support window function, and thus it cannot implement more complicated ordered operation. In order to implement such operation, it has to use other methods like dialect SQL or hard coding, which is far from the original intention of Hibernate, and the development efficiency is very low.

SPL code:

tbl.sort(sales,month).derive(if(sales==sales[-1],(amount-amount[-1])/amount[-1]):loopRate)

SPL is a professional business logic development technology, and good at performing ordered operation. In the above code, [-1] represents the previous record of the current record, which is the representation method of relative position, and easier to understand than the lag function (absolute position-moving).

Flow processing

Both Hibernate and SPL provide the general judgment statements and loop statements including if, ternary judgment, for, case, etc., which can process any complex flow, and we will not discuss them in this article. Now we will focus on comparing the convenience of the loop structure for set data.

Hibernate supports the loop function forEach, which is optimized for structured data objects. For example, the following code is to calculate the bonus according to certain rules:

Orders.forEach(r->{
    Double amount=r.getAmount();
    if (amount>10000) {
       r.setBonus(amount*0.05);
    }else if(amount>=5000 && amount<10000){
       r.setBonus (amount*0.03);
    }else if(amount>=2000 && amount<5000){
       r.setBonus(amount*0.02);
    }
});

The parameter of forEach is the Lambda expression, which can simplify the definition of function, conveniently process each member of set object (the loop variable r in the code). The use of forEach function and Lambda syntax in a combined way makes the overall code simpler than traditional loop statements. However, it should be noted that when the field is used in the forEach function, it needs to attach the name of loop variable, which is unnecessary for single-table calculation. For SQL that also uses Lambda syntax, the name of loop variable can be omitted. In fact, defining the name of loop variables is also unnecessary, and defining it in SQL is not needed. All these shortcomings indicate that Hibernate is not very professional in flow processing.

SPL also has the loop function optimized for structured data objects, which is directly represented in parentheses. For the same calculation task, SPL code:

Orders.(Bonus=if(Amount>10000,Amount*0.05,
if(Amount>5000 && Amount<10000, Amount*0.03,
if(Amount>=2000 && Amount<5000, Amount*0.02)
)))

Likewise, SPL’s loop function supports Lambda expression, and its interface is simpler, and there is no need to define the loop variables, and no need to reference the variable name when using the field. Thus, SPL is more convenient and professional than Hibernate. In addition to loop function, SPL boasts more professional flow processing functions, such as: taking a batch of records rather than one record in each round of loop; looping one round when the value of a certain field changes.

Function and syntax

Time and string functions

The time and string functions built in Hibernate is few, and many commonly used functions are not supported directly in Hibernate. Therefore, it has to use the dialect SQL or hard coding to implement such functions such as: getting a date before or after a specified date, getting the ordinal number of a date in the year, getting which quarter the date belongs to, string replacement, left truncation and getting corresponding ASCII code.

Let’s take “getting a date before or after a specified date” as an example. Assuming that the database is MySQL, first, you should create a custom dialect class that inherits from the parent class org.hibernate.dialect.MySQLDialect, next, register the name of HQL function (such as udf_dateadd) in the custom dialect class, and then introduce MySQL’s function date_add that is used for getting the date before or after a specified date. The key code is:

registerFunction("udf_dateadd", new SQLFunctionTemplate( DateType.INSTANCE,"date_add(?1,INTERVAL ?2 DAY)") );

Write HQL code during actual calculation.

select udf_dateadd (orderDate,3) from OrdersEntity

The above code is not only cumbersome, but cannot be migrated. Once the database is replaced, rewriting the custom dialect class is required.

On the contrary, SPL has rich built-in date and string functions, including the function for getting a date before or after a specified date. The same function code:

Orders.new(elapse(ORDERDATE,3))

This SPL code has nothing to do with database, and can be migrated between different databases without modifying.

Similarly, SPL also directly supports the functions such as getting the ordinal number of a date in the year, getting which quarter the date belongs to, string replacement, left truncation and getting corresponding ASCII code, and there is also no need to modify the code during migration.

SPL function options and cascaded parameter

It is worth mentioning that in order to further improve the development efficiency, SPL provides the unique function syntax.

When there are a large number of functions with similar functionality, most programming languages can only use different names or parameters to distinguish, which is not convenient in use, whereas SPL provides very unique function option, which allow the functions with similar functionality to share one function name, and their difference can be distinguished just by the option. For example, the basic function of the select function is to filter, if you only want to filter out the first record that meets the conditions, you can use the option @1:

T.select@1(Amount>1000)

When using the binary search to quickly filter the sorted data, you can use the option @b:

T.select@b(Amount>1000)

The options of function can also be used in a combined way, for example:

Orders.select@1b(Amount>1000)

The parameters of some functions are very complex and may be divided into multiple layers. In view of this situation, conventional programming languages do not have a special syntax solution, and can only generate multi-layer structured data object and then pass them in, which is very troublesome. However, SQL uses the keywords to separate the parameters into multiple groups, which is more intuitive and simpler, but it will use a lot of keywords, making the statement structure inconsistent. SPL creatively invents the cascaded parameter, which simplifies the expression of complex parameter. It can divide the parameters into three layers from high to low by semicolons, commas and colons:

join(Orders:o,SellerId ; Employees:e,EId)

A complete coding example in SPL

Based on professional structured data objects, and combined with professional structured data objects and flow processing functions, SPL can greatly improve the development efficiency of data business logic. For example: to calculate the bonus according to the rules, and insert the records containing the bonus field into the database, it needs multiple files and a large amount of code to implement in Hibernate, while it is much simpler in SPL:


A

B

C

1

=db=connect@e("dbName")


/Connect to database and begin transaction

2

=db.query@1 ("select sum(Amount) from sales

where
sellerID=? and year(OrderDate)=? and month(OrderDate)=?",
p_SellerID,year(now()),month(now()))


/Query sales amount of the current month

3

=if(A2>=10000 :200, A2<10000 && A2>=2000 :100, 0)


The cumulative bonus of the current month/

4

=p_Amount*0.05


The fixed bonus for the current order/

5

=BONUS=A3+A4


/Total bonus

6

=create(ORDERID,CLIENT,SELLERID,AMOUNT,BONUS,ORDERDATE)


/Create data structure of the order table

7

=A6.record([p_OrderID,p_Client,p_SellerID,p_Amount,BONUS,
date(now())])


/Generate an order record

8

>db.update@ik(A7,sales;ORDERID)


/Try to update the order record to database table

9

=db.error()


/Result of writing to database

10

if A9==0

>A1.commit()

/Commit the transaction if the writing action succeeds

11

else

>A1.rollback()

/Rollback the transaction if the writing action fails

12

>db.close()


/Close database connection

13

return A9


/Return the result of writing to database

Application framework

Java integration

Hibernate code itself is Java, and can be called directly by other Java codes.

SPL is a JVM-based data computing language, and provides an easy-to-use JDBC driver that can be seamlessly integrated in JAVA code. For example, store SPL code as a script file, and call the file name in JAVA as the form of stored procedure:

Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("call getClient()");

Hot deployment

Hibernate (Java) is a compiled language and does not support hot deployment. After modifying the code, it needs to recompile and restart the whole application, which increases the maintenance difficulty and reduces the system stability.

SPL is an interpreted language, and its code is placed outside the JAVA as the form of script file. SPL supports hot deployment. After modifying the code, it does not need to compile, nor does it to restart the application. Since SPL code does not depend on JAVA, and the business logic and front-end code are physically separated, the coupling is lower.

Code migration

Code migration is one of the design goals of Hibernate, that is, allow the business logic to be seamlessly switched between different databases. However, due to the lack of ability to calculate the structured data, Hibernate generally uses dialect SQL to deeply binds the databases in practice, which makes Hibernate basically lose the ability to migrate the code, unless the business logic is simple enough.

In comparison with Hibernate, SPL is more powerful in computing ability, and can implement complex structured data calculation only with its rich built-in function library, and there is no need to use SQL. Such calculation code can be seamlessly migrated between databases. In the code for fetching the data from database, SPL usually uses the query function to execute dialect SQL to generate the table sequence. Although it is relatively simple for SQL to fetch the data, and not difficult to manually migrate the code, it still needs a certain amount of work. In order to make the data-fetching code easy to be migrated, SPL specially provides the general SQL that does not depend on the specific database, which allows you to seamlessly migrate the code between mainstream databases.

By comparing Hibernate and SPL in many aspects, we know that Hibernate extremely lacks the professional ability in the development of business logic for the database, and the development efficiency is low. In contrast, SPL has the advantages of concise syntax, high expression efficiency, convenient code migration, more professional structured data objects, richer functions, more powerful computing ability and more convenient flow processing, the development efficiency is much higher than that in Hibernate.