New ways to use databases in Java programs

  • 2020-04-01 03:58:20
  • OfStack

  Java 8 is finally here! After years of waiting, Java programmers can finally get the support of functional programming in Java. Functional programming support can streamline the existing code and provides powerful ability for Java. In the most of these new features to the operation of the database is a Java programmer. Brought exciting functional programming is simple and efficient database API. 8 will support Java language such as c # LINQ competition with the new database access.
A functional way of processing data

Java 8 not only adds functional support, it also extends the Collection class with a new functional way of handling data, whereas Java typically requires a lot of loops and iterators to handle large amounts of data.


For example, suppose you have a collection of Customer objects:
 


Collection<Customer> customers;

If you are only interested in customers from Belgium, you will have to iterate over all customer objects and save only what you need.
 


Collection<Customer> belgians = new ArrayList<>();
for (Customer c : customers) {
  if (c.getCountry().equals("Belgium"))
    belgians.add(c);
}

Not only does it take five lines of code, but it's also not very abstract. Can you speed things up by filtering all objects by two threads concurrently? Then you'll have to use a lot of dangerous multi-threaded code to rewrite everything.

With Java 8, you can do the same thing with just one line of code. With support for functional programming, Java 8 lets you write a function that indicates which clients (objects) you are interested in and then filter the collection using that function.
 


customers.stream().filter(
  c -> c.getCountry().equals("Belgium")
);

Java 8 version of the code above is not only shorter, and easier to understand. It's almost no cliches (circular or iterator, etc). The code calls the filter () method, then obviously this code is used to filter the customer (objects). You don't need to waste time on reading the code of cycle to understand what to do it on the data of it.

What if you want to execute this code concurrently? You just use another type of stream
 


customers.parallelStream().filter(
  c -> c.getCountry().equals("Belgium")
);

Even more exciting is that this functional style of code also applies to databases

Use a functional approach on the database

Traditionally, programmers have required special database query statements to access database data. For example, the following is JDBC code to find customers from Belgium:
 


PreparedStatement s = con.prepareStatement(
   "SELECT * "
  + "FROM Customer C "
  + "WHERE C.Country = ? ");
s.setString(1, "Belgium");
ResultSet rs = s.executeQuery();

Most of the code is a string, it will make the compiler cannot find errors and the sloppy code can cause security problems. And these lots of boilerplate code makes writing data access code is redundant. Some tools such as jOOQ, through the use of special Java library to provide the database query language can solve the problem of error checking and security. Or you can use object-relational mapping tools to get rid of a lot of boring code, but they can only be used for general-purpose access queries, or special database query languages if complex queries are required.

With Java 8, you can use the streaming API to query the database in a functional way. For example, Jinq is an open source project that explores how future database apis can make functional programming possible. Here is a database query using Jinq:
 


customers.where(
  c -> c.getCountry().equals("Belgium")
);

In fact, future versions of Jinq will allow you to write database queries directly using the streaming API. When the code runs, Jinq will automatically translate to the database query code, just as JDBC queries did before.


This way, you can write efficient database queries without learning any new database query languages. You can use the same style of code for Java collections. You don't need a special Java compiler or virtual machine. All the code is compiled and run on the regular Java 8 JDK. If your code has errors, the compiler will find them and report them to you, just like regular Java code.

Jinq supports complex queries like SQL92. It also supports Selection, projection, joins, and subqueries. The algorithm that translates Java code into database queries is very flexible and can translate anything it can accept. For example, Jinq can translate the following database query, although it is complex.
 


customers
  .where( c -> c.getCountry().equals("Belgium") )
  .where( c -> {
    if (c.getSalary() < 100000)
      return c.getSalary() < c.getDebt();
    else
      return c.getSalary() < 2 * c.getDebt();
    } );

As you can see, functional programming in Java 8 is perfect for database queries. And the queries are compact, even complex queries can be competent.

Inner workings

But how does it all work? How do you get a regular Java compiler to turn Java code into a database query? What is special about Java 8 that makes this possible?

The key to the new database PI that supports these functional styles is a bytecode analysis technique called "symbolic execution." While your code is compiled by a normal Java compiler and runs in a normal Java virtual machine, Jinq is able to analyze and build database queries from your compiled Java code as it runs. When using the Java 8 Streams API, you often find that symbolic execution works best when analyzing short functions.

The easiest way to understand how this symbolic execution works is to use an example. Let's examine how the following query is converted by Jinq to the SQL query language:
 


customers
  .where( c -> c.getCountry().equals("Belgium") )

Initially, the variable customers is a collection, and its corresponding database query is:
 


SELECT *
 FROM Customers C

Then, the where() method is called and a function is passed to it. In the where() method, Jinq opens the.class file for the function and gets the bytecode that the function is compiled into for analysis. Instead of using real bytecode in this example, let's use some simple instructions to represent the bytecode of this function:


  d = c.getCountry()

  e = “Belgium”

  e = d.equals(e)

  return e

Here, we assume that the function has been compiled by the Java compiler into these four instructions. That's what Jinq sees when it calls the where() method. How can Jinq understand this code?

Jinq is analyzed by executing code. But Jinq does not run the code directly. It runs the code "abstractly" : instead of using real variables and real values, Jinq USES symbols to represent all the values when the code is executed. This is why this analysis is called "symbolic execution".

Jinq executes each instruction and keeps track of all the side effects or everything that the code changes as the program is in state. The following is a chart showing all the side effects that Jinq finds when executing these four lines of code in a symbolic execution.

Example of symbolic execution

In the figure, you can see that after the first instruction runs, Jinq finds two side effects: the variable d has changed, and the method customer.getcountry () is called. Since it is a symbolic execution, variable d does not give a true value such as "USA" or "Denmark", which is assigned to the symbolic value of c. geetcountry ().

After all of these instructions are symbolically executed, Jinq makes side-effects minimal. Since variables d and e are local variables, any changes to them are discarded after the function exits, so these side effects are negligible. Jinq also knows that the customer.getcountry () and string.equals () methods do not modify any variables or display any output, so these method calls can also be ignored. From this, Jinq can conclude that executing this function produces only one effect, which returns c.getcountry ().equals("Belgium").

Once Jinq has understood the function passed to it in the where () method, it can mix its knowledge of database queries and create a new database query before the customers set.

Generating database queries


This is how Jinq generates database queries from your code. The use of symbolic execution means that this approach is quite powerful for different code patterns that are output from different Java compilers. If the code Jinq encounters has the side effect of not being translated into a database query, Jinq will keep your code unchanged. Because everything is written in normal Java code, Jinq can run that code directly, and your code will produce the expected results.

This simple translation example should give you an idea of how to query translated works. You can be sure that these algorithms will correctly generate database queries from your code.
prospect

I hope I've given you a taste of Java 8's new way of working with databases in Java. Java 8 supports functional programming that allows you to write code for a database in the same way you would code for a Java collection. Hopefully, the existing database apis will soon be extended to support these types of queries.


Related articles: