java-databases.adoc
java-databases.adoc
Marco Behler
2022-08-24
:page-layout: layout-guides
:page-icon: design_app
:sourcedir: /mnt/c/dev/database-access/src/com/company
:linkattrs:
:page-image: "/images/guides/undraw_server_status_5pbv.png"
:page-description: You can use this guide to get an overview of all popular
database libraries & APIs in Java. Covers JDBC, Hibernate, JPA, jOOQ, Spring Data
and more.
:page-published: true
:page-serieslink: https://www.marcobehler.com/courses/2-hibernate-jpa-getting-
started?pk_campaign=Guide-Companion
:page-seriesdescription: "Watch the Java First section of this guide as
screencasts"
:page-tags: ["java", "databases"]
:page-commento_id: /guides/java-database-access-an-overview-guide
[*Editor’s note*: At ~6,500 words, you probably don't want to try reading this on a
mobile device. Bookmark it and come back later.]
Whenever you want to access a database with your (server or desktop) Java
application three questions pop up:
[source,java,role=tooth]
----
public class User {
// Constructor/Getters/Setters....
----
{empty} +
[[example-table]]
In addition to your Java class, you also have a _USERS_ database table. Imagine you
have three users (i.e. rows) saved to your table, which means it looks roughly like
this:
.Users
[.table]
|===
|*id*|*first_name*|*last_name*
|1|hansi|huber
|2|max|mutzke
|3|donald|trump
|===
How do you now map between your Java class and that table?
We will cover all the different options in this guide, but it is very important to
understand JDBC basics first.
Why? Because every other library, be it Spring or Hibernate, builds _on top_ of
these basics - they all use JDBC under the hood.
[[plain-jdbc]]
== JDBC: Low-Level Database Access
The most low-level way to accessing databases in Java, is through the JDBC API
(https://docs.oracle.com/javase/tutorial/jdbc/basics/index.html[Java Database
Connectivity]).
Every framework that you encounter later on in this article uses JDBC under the
hood. But you can, of course, choose to use it directly to execute your SQL
queries.
The good thing about JDBC: You don't need any 3rd-party libraries as it comes with
https://www.marcobehler.com/guides/a-guide-to-java-versions-and-features[every
JDK/JRE]. You only need an appropriate JDBC driver for your specific database.
*Recommendation:* If you want to get a good overview of how to get started with
JDBC, where to find drivers, setup connection pools & information on executing SQL
queries, I recommend you to read my https://www.marcobehler.com/guides/jdbc[What is
JDBC?] article first, and then continue with this article.
Imagine you have a database containing that `_Users_` table from above. You want to
write a query that selects all Users from that table and converts them to a
`_List<User_`, a list of Java objects.
A small spoiler: JDBC does not help you at all converting from SQL to Java Objects
(or the other way around). Let's see some code:
[source,java]
----
include::https://raw.githubusercontent.com/marcobehler/jdbc-article/master/src/
main/java/com/marcobehler/JdbcQueries.java[]
----
[source,java,indent=0,role=tooth]
----
include::https://raw.githubusercontent.com/marcobehler/jdbc-article/master/src/
main/java/com/marcobehler/JdbcQueries.java[lines=10..13]
----
Here we are opening a database connection against a MySQL database. You should not
forget to wrap your `_DriverManager.getConnection_` call in a
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[
try-with-resources] block, so your connection gets automatically closed again, once
you are done with your code block.
[source,java,indent=0,role=tooth]
----
include::https://raw.githubusercontent.com/marcobehler/jdbc-article/master/src/
main/java/com/marcobehler/JdbcQueries.java[lines=14..15]
----
You need to create and execute your SQL statement, which you do by creating and
executing a Java PreparedStatement. (PreparedStatements allow you to have `_?_`
placeholders in your SQL statements, but we'll ignore that for now.)
[source,java,indent=0,role=tooth]
----
include::https://raw.githubusercontent.com/marcobehler/jdbc-article/master/src/
main/java/com/marcobehler/JdbcQueries.java[lines=17..26]
----
You need to manually traverse through the ResultSet (i.e. all rows that your SQL
query returns) and then create your Java user objects by hand, by calling the right
getters on each ResultSet row, with the correct column names AND types
(`_getString()_`, `_getInt()_`).
In addition, we also conveniently left out two things in our code above:
* Placeholders in (more complex) SQL queries (think: `_select * from USERS where
name = ? and registration_date = ?_`), as you want to protect against
https://www.w3schools.com/sql/sql_injection.asp[SQL injection].
* Transaction handling, which includes opening and committing transactions, as well
as rolling them back, whenever an error happens.
However, the example above demonstrates rather well, why JDBC is considered to be
rather low-level. Because you have a lot of manual work to do to go from SQL<>Java.
mb_ad::jdbc_workbook[]
=== JDBC Summary
When using JDBC, you are basically working with bare metal. You have the full power
and speed of SQL and JDBC at your hand, but you need to make sure to somehow
convert back and forth between your Java Objects and the SQL code yourself.
You also have to make sure to be a good developer and open/close database
connections yourself.
That's where more convenient, light-weight frameworks come in, which we will cover
in the next section.
Java developers are _usually_ more comfortable with writing Java classes, than with
writing SQL statements. Hence many (greenfield) projects are written with a Java-
First approach, which means that you create your Java class _before_ you create the
corresponding
database table.
This naturally leads to the object-relational mapping question: How do you map from
your newly written Java class to your (yet to be created) database table? And could
you even generate your database _from_ those Java classes? At least, initially?
Even though countless books have been written about it, here is the attempt to
summarize what Hibernate does well:
1. It lets you (relatively) easily convert between database tables and java
classes, without you having to do much apart from an initial mapping.
2. It allows you to not have to write any SQL code for (basic) CRUD operations.
Think: creating a user, deleting a user, updating a user.
3. It offers a couple query mechanisms (HQL, Criteria API) on top of SQL to query
your databases. For now, let's say these are "object-oriented" versions of SQL,
even though this is a bit meaningless without examples, which will follow later.
Finally, let's look at some code. Imagine, you have the following database table,
which is basically the same table you used in the <<plain-jdbc,plain JDBC
section>>.
[source,sql,role=tooth]
----
create table users (
id integer not null,
first_name varchar(255),
last_name varchar(255),
primary key (id)
)
----
[source,java,role=tooth]
----
public class User {
Out of the box, Hibernate obviously has no way of knowing which of your classes
should be mapped how to database tables. Should the User.java class be mapped to a
_invoices_ database table or to a _users_ table?
Historically, to let Hibernate know what it should do, you wrote mapping
https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/mapping.html[.xml
files].
We will not cover xml mapping in this guide, as for the past couple of years it has
been superseded with an _annotation-based_ mapping approach.
You might have encountered some of these annotations already, like _@Entity_, or
_@Column_, or _@Table_. Let's have a look at what our annotated User.java class
from above would look like with these mapping annotations.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.GeneratedValue;
import javax.persistence.Column;
import javax.persistence.Id;
@Entity
@Table(name="users")
public static class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
----
It would not be in the scope of this guide to go too much into detail of every
annotation, but here's a quick summary:
There's of course a lot more annotations, but you get the picture. With Hibernate
you write your Java classes, and then need to make sure to annotate them with the
correct annotations.
After having annotated your classes, you still need to bootstrap Hibernate itself.
Hibernate's entry point for pretty much everything is the so-called
_SessionFactory_, which you need to configure.
In newer Hibernate versions (>5.x), this code looks a bit ugly and libraries like
Spring will take care of this bootstrapping code for you. But, if you want to get
started with plain Hibernate, this is what you will need to do.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
----
Now that you have the mapping set-up and constructed a SessionFactory, the only
thing left to do is to get a session (read: database connection) from your
SessionFactory and then, for example, save the user to the database.
Yes, that is right, you do not need to write the SQL yourself anymore. Hibernate
will do that for you.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
Session session = sessionFactory.openSession();
User user = new User();
user.setFirstName("Hans");
user.setLastName("Dampf");
// this line will generate and execute the "insert into users" sql for you!
session.save( user );
----
Compared with plain JDBC, there is no more messing around with PreparedStatements
and parameters, Hibernate will make sure to create the right SQL for you (as long
as your mapping annotations are right!).
Let's have a look at what simple select, update and delete SQL statements would
look like.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
So far, we have only looked at basic persistence, like saving or deleting a User
object. But there are of course times, when you need more control and need to write
more complex SQL statements. For that, Hibernate offers its own query language, the
so called HQL (Hibernate Query Language).
HQL looks similar to SQL,but is focused on your Java objects and actually
independent of the underlying SQL database. That means, in theory, the same HQL
statements work for all databases (MySQL, Oracle, Postgres etc.), with the drawback
that you lose access to all database specific features.
Now what does "HQL is focused on Java" objects mean? Let's have a look at an
example:
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
Both queries look very much like their SQL equivalents, but note that you are not
accessing SQL tables or columns (first_name) in these queries.
Instead, you are accessing properties (u.firstName) of your mapped User.java class!
Hibernate will then make sure to convert these
HQL statements to proper, database specific SQL statements. And in the case of a
select automatically convert the returned rows as User objects.
When writing HQL statements, you are essentially still writing or concatenating
plain strings (though there is tooling support, from IDEs like IntelliJ). Making
your HQL/SQL statements dynamic (think: different where clauses depending on user
input) is the interesting part.
Writing criteria (v2) queries in Hibernate has a steeper learning curve and it
needs some more project setup. You need to setup an annotation processing plugin to
generate a "Static Metamodel" of your annotated classes (think: the User from
above). And then write some seriously complex queries with generated classes.
Let's have a look at our HQL select query from above, and how you would convert it
to criteria query.
[[criteria-2.0]]
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery( User.class );
Root<User> root = criteria.from( User.class );
criteria.select( root );
criteria.where( builder.equal( root.get( User_.firstName ), "hans" ) );
List<User> users = entityManager.createQuery( criteria ).getResultList();
----
As you can see, you basically trade in readability and simplicity for type-safety
and flexibility - e.g. feel free to sprinkle in if-elses to construct dynamic where
clauses on the fly.
But keep in mind, that for our basic example, you now have six lines of code: For a
simple "select * from users where firstName = ?".
Hibernate doesn't just offer simple mapping and querying features. Real-Life
mappings and queries will obviously be much more complex than the ones you found
above. In addition, Hibernate offers a ton of other _convenience_ features, like
cascading, lazy loading, caching and much more. It truly is a complex piece of
software, that you cannot fully grasp by just copy-and-pasting some online
tutorial.
2. Some developers think that you do not need to understand SQL anymore, when using
Hibernate. The reality is, the more complex your software gets, the _more_ SQL
skills you will need, to verify the SQL statements that Hibernate generates and
optimize them.
To combat these two issues, you only have one choice: To use Hibernate efficiently,
you need (to get) good knowledge of Hibernate *and* SQL.
[[hibernate-tutorials]]
=== What are good tutorials or books on Hibernate?
If you want more advanced information on Hibernate, make sure to check out
https://vladmihalcea.com/[Vlad Mihalcea's] or https://thoughts-on-java.org/[Thorben
Janssen's] sites. Both are experts on Hibernate and regularly publish awesome
Hibernate content.
If you like to watch video courses, you can also check out the
https://www.marcobehler.com/courses/2-hibernate-jpa-getting-started?
utm_campaign=database_guide&utm_medium=database_guide&utm_source=database_guide[Hib
ernate screencasts] on this site. They are not the brand-newest anymore, but give
you a super-fast quickstart into the Hibernate universe.
=== What is the Java Persistence API (JPA)?
So far, we only talked about plain Hibernate, but what about JPA? How does it
compare to a library like Hibernate?
In simpler words: If your library supports (e.g.) saving objects to the database in
a certain way, supports mapping and querying capabilities (like the criteria API
etc.) and much more, then you can call it fully https://download.oracle.com/otn-
pub/jcp/persistence-2_1-fr-eval-spec/JavaPersistence.pdf?
AuthParam=1578473058_9b3db74ec95ca7140809a2cd1c7e2609[JPA compliant.]
So, instead of writing Hibernate specific code, or EclipseLink specific code, you
write JPA specific code. And then it is just a matter of adding some libraries
(Hibernate) and configuration file to your JPA project, and you can access your
database. In practice, that means JPA is another abstraction _on top_ of Hibernate.
There's multiple blogs that sum up the changes in those specifications for you, but
https://vladmihalcea.com/[Vlad Mihalcea] and
https://thoughts-on-java.org/category/jpa/[Thorben Janssen] do it best.
=== What is the actual difference then between Hibernate and JPA?
So, as the JPA specification process itself takes time and the output is "just" a
common denominator of existing libraries, the features it offers are only a
_subset_ of e.g. all the features that Hibernate offers. Otherwise Hibernate and
EclipseLink and TopLink would be exactly the same.
* You either use JPA as much as possible, sprinkled in with Hibernate specific
features, where the JPA specification is lacking behind.
* Or use plain Hibernate all the way (my personal, preferred choice).
Let's have a look at the example from above, where we saved Users with the JDBC and
Hibernate API. Only this time, we save the users with the JPA API.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
EntityManagerFactory factory =
Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );
[source,java,role=tooth]
----
package org.hibernate;
// and
----
Simple as.
As already mentioned before, JPA comes with its own query language, the JPQL. It is
essentially a heavily-inspired-subset of Hibernate's HQL, with JPQL queries always
being valid HQL queries - but not the other way around.
Hence, both versions of the same query will literally look the same:
[source,java,role=tooth]
----
// HQL
int updatedEntities = session.createQuery(
"update Person " +
"set name = :newName " +
"where name = :oldName" )
.setParameter( "oldName", oldName )
.setParameter( "newName", newName )
.executeUpdate();
// JPQL
int updatedEntities = entityManager.createQuery(
"update Person p " +
"set p.name = :newName " +
"where p.name = :oldName" )
.setParameter( "oldName", oldName )
.setParameter( "newName", newName )
.executeUpdate();
----
There are more JPA implementations out there, than just Hibernate. Two primarily
come to mind: http://www.eclipse.org/eclipselink/[EclipseLink] (see
https://thoughts-on-java.org/difference-jpa-hibernate-eclipselink/[Hibernate vs
Eclipselink]) and the (older)
http://www.oracle.com/technetwork/middleware/toplink/overview/index.html[TopLink].
They lack behind in market-share compared to Hibernate, but you'll also find
projects using them in corporate settings. There are also other projects like
https://github.com/BatooOrg/BatooJPA[BatooJPA], but you will find in most cases
that these libraries are abandoned and not maintained anymore, because it is quite
some effort to maintain a fully JPA compliant library.
You most certainly need an active community to support further development of your
library and Hibernate probably has the most active community as of 2020.
[[querydsl]]
=== QueryDSL
How does a library like http://www.querydsl.com/[QueryDSL] fit into the JPA section
of this guide? Up until now, we constructed either HQL/JPQL queries by hand (read:
string concatenation), or through the rather complex Criteria (2.0) API.
QueryDSL tries to give you the best of both worlds. Easier construction of queries
than with the Criteria API, and more type-safety and less fumbling around than with
plain strings.
It should be noted, that QueryDSL was unmaintained for a while, but starting 2020,
has picked up steam again. And that it does not only support JPQ, but also NoSQL
databases like MongoDB or Lucene.
Let's look at some example QueryDSL code, which runs a "select * from users where
first_name = :name" SQL statement.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
QUser user = QUser.user;
JPAQuery<?> query = new JPAQuery<Void>(entityManager);
List<User> users = query.select(user)
.from(user)
.where(user.firstName.eq("Hans"))
.fetch();
----
Where does the QUser class come from? QueryDSL will automatically create that for
you from your JPA/Hibernate-annotated User class, during compile time - with the
help of an appropriate annotation processing compiler plugin.
You can then use these generated classes to execute type-safe queries against the
database. Don't they read much nicer than the JPA Criteria 2.0 equivalent?
ORM frameworks are mature and complex pieces of software. The major caveat is to
think one does not need to understand SQL anymore, when you are working with any of
the mentioned JPA implementation.
It is true, ORMs offer you a fast start when trying to map basic classes to
database tables. But coupled with a lack of basic knowledge about how they work,
you will run into _major_ performance and maintenance challenges later in your
project.
*##Main Takeaway##*
Make sure you get a good foundation on how e.g. <<hibernate-tutorials, Hibernate>>
works *AND* how SQL and your database works. Then you are going to be on the right
way.
They work well if you have an existing (legacy) database, or even if you start a
new project from scratch, where you design and write the database schema _before_
writing the corresponding Java classes.
=== jOOQ
2. Instead of writing SQL String statements with plain JDBC, you will use those
generated Java classes to write your SQL queries.
3. jOOQ will conveniently turn these Java classes and queries into real SQL,
execute them against the database and map the results back to Java code.
So, imagine you setup jOOQ's code generator and let it run against the <<example-
table,Users>> table that was introduced at the beginning of this guide. jOOQ will
generate its own USERS table class, which e.g. allows you to execute the following,
type-safe query against the database:
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
// "select u.first_name, u.last_name, s.id from USERS u inner join SUBSCRIPTIONS s
// on u.id = s.user_id where u.first_name = :name"
Result<Record3<String, String, String>> result =
create.select(USERS.FIRST_NAME, USERS.LAST_NAME, SUBSCRIPTIONS.ID)
.from(USERS)
.join(SUBSCRIPTIONS)
.on(USERS.SUBSCRIPTION_ID.eq(SUBSCRIPTIONS.ID))
.where(USERS.FIRST_NAME.eq("Hans"))
.fetch();
----
jOOQ not only helps you build and execute SQL statements against your database
schema, it also helps you with CRUD statements, mapping between Java POJO's and
database records.
It also will help you access all of your database's (vendor specific) features
(think window functions, pivots, flashback queries, OLAP, stored procedures,
vendor-specific functions etc.)
mb_ad::jooq_course[]
=== MyBatis
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
package org.mybatis.example;
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUser(int id);
}
----
That interface then allows you to write code like the following:
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUser(1);
----
MyBatis also has mapping functionalities built-in, i.e. it can convert from a table
to a User object. But only in simple cases, where e.g. the column names match or
similar. Otherwise you will have to specify the mappings yourself, in one of the
XML configuration files.
[[jdbi]]
=== Jdbi
Its API comes in two flavors, which will give you a rough idea of how to work with
it.
[source,java,role=tooth]
.(Fluent API Parental Advice: Don't just blindly copy this code)
----
Jdbi jdbi = Jdbi.create("jdbc:h2:mem:test"); // (H2 in-memory database)
Second, the _declarative API_, which you can see link:#[in action
here,role="solution-link"].
[[solution8]]
[source,java,role=tooth]
.(Declarative API Parental Advice: Don't just blindly copy this code)
----
// Define your own declarative interface
public interface UserDao {
@SqlUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR)")
void createTable();
[[fluent-jdbc]]
=== fluent-jdbc
[source,java,role=tooth]
.*(Declarative API Parental Advice: Don't just blindly copy this code)*
----
FluentJdbc fluentJdbc = new FluentJdbcBuilder()
.connectionProvider(dataSource)
.build();
query
.update("UPDATE CUSTOMER SET NAME = ?, ADDRESS = ?")
.params("John Doe", "Dallas")
.run();
=== SimpleFlatMapper
[source,java,role=tooth]
----
// will map the resultset to User POJOs
JdbcMapper<DbObject> userMapper =
JdbcMapperFactory
.newInstance()
.newMapper(User.class)
One of the oldest helper classes in Spring (more specifically in the spring-jdbc
dependency) is called the JDBCTemplate. It has been around since 2001 and should
_not_ be confused with Spring Data JDBC.
It comes in two flavors, the JdbcTemplate and its (wrapping) cousin, the
NamedParameterJdbcTemplate. Let's have a look at some code examples, to find out
how you would query your database with both.
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
jdbcTemplate.query(
"SELECT id, first_name, last_name FROM users WHERE first_name = ?", new
Object[] { "Josh" },
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("first_name"),
rs.getString("last_name"))
).forEach(user -> log.info(user.toString())); <3>
----
1. Here, we are executing a create table statement. Note that compared with plain
JDBC, you do not have to catch SQLExceptions anymore, as Spring will convert them
to Runtime Exceptions for you.
3. Here, we are using RowMappers, to convert the plain JDBC ResultSet to User POJOs
from our project.
By looking at that code, you can see that JDBC Template is really just a nifty
wrapper _around_ the JDBC API, which shows its strengths especially inside Spring
projects (e.g. where you can use the @Transactional annotation).
Let's have another look at our JPA example from before, where you manually open and
commit a transaction, through the EntityManager (remember: EntityManager is really
just a Hibernate session, which is a JDBC connection on steroids).
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
EntityManagerFactory factory =
Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void doSomeBusinessLogic() {
entityManager.persist( new Event( "Our very first event!", new Date() ) );
entityManager.persist( new Event( "A follow up event", new Date() ) );
}
----
Reads a lot nicer, doesn't it? There is obviously _much more_ to transaction
handling than that, but only so much can be covered in this guide.
If you are really keen on understanding transactions and actually getting your
hands dirty, then you might want to have a look my e-book
https://www.marcobehler.com/books/1-java-database-connections-transactions?
utm_campaign=database_guide&utm_medium=database_guide&utm_source=database_guide[Jav
a Database Connections & Transactions]. In it, you will find a ton of code examples
and exercises to practice proper transaction handling.
1. Two popular ones, that we are going to cover in this guide: _Spring Data JDBC_
and _Spring Data JPA_.
2. Many more, like Spring Data REST or Spring Data Redis, or even Spring Data LDAP.
Check out the website for a complete list.
At their core, all Spring Data projects make it really simple _to write
repositories or DAOs_ and SQL queries. (There is a bit more to it, but for the sake
of this guide, let's keep it at that.)
If you had a User.java class, you would also have a UserRepository. That
UserRepository would then have methods like findByEmail, findById etc. In short, it
allows you to execute all SQL operations in regard to your Users table.
[source,java,role=tooth]
----
User user = userRepository.findByEmail("my@email.com")
----
The interesting thing about Spring Data is, that it understands the JPA mapping
annotations of your User class (remember @Entity, @Column, @Table etc.) and
automatically generates repositories for you!
This means, you get all the basic CRUD operations (save, delete, findBy) for free,
without you having to write any extra code.
Let's have a look at some code examples. Assuming you would have the appropriate
spring-data-{jdbc|jpa} .jars on your classpath, with a bit of configuration added,
you could then write code like this:
[source,java,role=tooth]
.*(Parental Advice: Don't just blindly copy this code)*
----
import org.springframework.data.jpa.repository.JpaRepository;
import com.marcobehler.domain.User;
// JpaRepository contains all of these methods, you do not have to write them
yourself!
List<T> findAll();
// and many more...that you can execute without implementing, because Spring
Data JPA will
// automatically generate an implementation for you - at runtime
}
----
Your custom repository simply has to extend Spring Data's JPARepository (in case
you are using JPA), and you will get the find*/save* (and more) methods for free,
as they are part of the JPARepository interface - I just added them in the code
above for clarity.
Even better, you can then write custom JPA queries (or custom SQL queries) by
convention. This is very similar to writing queries in
https://rubyonrails.org/[Ruby on Rails], for example. How?
[source,java,role=tooth]
----
import org.springframework.data.jpa.repository.JpaRepository;
import com.marcobehler.domain.User;
Spring will, on application start-up, read in that method name, and translate it to
the appropriate JPA query, whenever you execute that method. (resulting SQL:
"select * from Users where email_address = :emailAddress and lastName = :lastName).
It is a bit different with Spring Data JDBC (other than having to extend from a
different interface, `_CrudRepository_`). Spring Data JDBC does not support writing
these query methods. Instead, you'll have to write the query yourself, which will
directly be executed as a JDBC query, not a JPA query.
[source,java,role=tooth]
----
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.jdbc.repository.Query;
import com.marcobehler.domain.User;
You can do the same with Spring Data JPA as well (note the different import for the
`_Query_` annotation and that we're not using named parameters here for a change).
You still won't need an implementation of that interface.
[source,java,role=tooth]
----
import org.springframework.data.jpa.repository.Query;
1. At its core, Spring Data is all about simple data access and thus repositories
and queries. It understands javax.persistence mapping annotations and, from these
annotations, generates DAOs for you.
2. Spring Data JPA is a convenience library _on top_ of JPA/Hibernate. It is not
that both libraries are different, but rather, they _integrate_. It lets you write
super-simple JPA repositories, while being able to access the full feature-set of
your ORM.
3. Spring Data JDBC is a convenience library _on top_ of JDBC. It allows you to
write JDBC-based repositories, _without_ needing a full blown ORM and its features
(caching, lazy loading..). This means more control and less magic behind the
scenes.
And most of all, Spring Data integrates nicely with any other Spring project,
though it is the obvious choice, whenever you are creating
https://spring.io/projects/spring-boot[Spring Boot] projects.
Again, this guide can only give you a quick overview of what Spring Data is, for
more details, have a look at the https://spring.io/projects/spring-data[official
documentation].
== Video
If you want to see Hibernate/JPA in action, have a look at the video below.
mb_youtube::xHminZ9Dxm4[]
By now, you might feel a bit overwhelmed. A ton of different libraries and then
even more convenience options on top. But summing everything up, here's a couple of
rough guidelines ( and as you might have guessed, there is not just the one and
only right way) :
* No matter what database library you ultimately decide on, make sure you have a
solid understanding of SQL and databases (which Java developers _usually_ haven't).
* Choose a library which has a vibrant community, good documentation and regular
releases.
* Learn your database libraries inside out, i.e. spend the time to read those 608
JPA pages.
* Your project will be fine with either plain Hibernate or Hibernate wrapped in
JPA.
* It will also be fine with JooQ or any of the other mentioned database-first
libraries.
* You can also combine those libraries, e.g. a JPA implementation and JoOQ or plain
JDBC - or add more convenience with libraries like QueryDSL.
That's it for today. If you have any questions or if you found some spelling errors
just post them to the comment section or mailto:marco@marcobehler.com[e-mail me].
A big thanks goes out to the following readers for their detailed feedback &
corrections to this guide: https://www.reddit.com/user/parms[parms],
https://www.reddit.com/user/lukaseder[lukaseder],
https://twitter.com/StewAshton[Stew Ashton].