Persistence top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

There are many ways we can connect to a MySQL database from Java and in this tutorial, we're going to explore several options to see how to achieve this.

We'll start by looking at arguably the most popular options using JDBC and Hibernate.

Then, we'll also look at some external libraries including MyBatis, Apache Cayenne and Spring Data. Along the way, we'll provide a number of practical examples.

2. Preconditions

We'll assume that we already have a MySQL server installed and running on localhost (default port 3306) and that we have a test schema with the following person table:

CREATE TABLE person 
( 
    ID         INT, 
    FIRST_NAME VARCHAR(100), 
    LAST_NAME  VARCHAR(100)  
);

We'll also need the mysql-connector-java artifact which as always is available from Maven Central:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>

3. Connecting Using JDBC

JDBC (Java Database Connectivity) is an API for connecting and executing queries on a database.

3.1. Common Properties

During the course of this article, we'll typically use several common JDBC properties:

  • Connection URL – a string that the JDBC driver uses to connect to a database. It can contain information such as where to search for the database, the name of the database to connect to and other configuration properties:
    jdbc:mysql://[host][,failoverhost...]
        [:port]/[database]
        [?propertyName1][=propertyValue1]
        [&propertyName2][=propertyValue2]...

    We'll set this property like so: jdbc:mysql://localhost:3306/test?serverTimezone=UTC

  • Driver class – the fully-qualified class name of the driver to use. In our case, we'll use the MySQL driver: com.mysql.cj.jdbc.Driver
  • Username and password – the credentials of the MySQL account

3.2. JDBC Connection Example

Let's see how we can connect to our database and execute a simple select-all through a try-with-multiple-resources:

String sqlSelectAllPersons = "SELECT * FROM person";
String connectionUrl = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";

try (Connection conn = DriverManager.getConnection(connectionUrl, "username", "password"); 
        PreparedStatement ps = conn.prepareStatement(sqlSelectAllPersons); 
        ResultSet rs = ps.executeQuery()) {

        while (rs.next()) {
            long id = rs.getLong("ID");
            String name = rs.getString("FIRST_NAME");
            String lastName = rs.getString("LAST_NAME");

            // do something with the extracted data...
        }
} catch (SQLException e) {
    // handle the exception
}

As we can see, inside the try body, we iterate through the result set and extract the values from the person table.

4. Connecting Using ORMs

More typically, we'll connect to our MySQL database using an Object Relational Mapping (ORM) Framework. So, let's see some connection examples using the more popular of these frameworks.

4.1. Native Hibernate APIs

In this section, we'll see how to use Hibernate to manage a JDBC connection to our database.

First, we need to add the hibernate-core Maven dependency:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.10.Final</version>
</dependency>

Hibernate requires that an entity class must be created for each table. Let's go ahead and define the Person class:

@Entity
@Table(name = "Person")
public class Person {
    @Id
    Long id;
    @Column(name = "FIRST_NAME")
    String firstName;

    @Column(name = "LAST_NAME")
    String lastName;
    
    // getters & setters
}

Another essential aspect is to create the Hibernate resource file, typically named hibernate.cfg.xml, where we'll define configuration information:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/test?serverTimezone=UTC</property>
        <property name="connection.username">username</property>
        <property name="connection.password">password</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!-- Validate the database schema on startup -->
        <property name="hbm2ddl.auto">validate</property>

        <!-- Names the annotated entity class -->
        <mapping class="Person"/>
    </session-factory>
</hibernate-configuration>

Hibernate has many configuration properties. Apart from the standard connection properties, it is worth mentioning the dialect property which allows us to specify the name of the SQL dialect for the database.

This property is used by the framework to correctly convert Hibernate Query Language (HQL) statements into the appropriate SQL for our given database. Hibernate ships with more than 40 SQL dialects. As we're focussing on MySQL in this article, we'll stick with the MySQL5Dialect dialect.

Finally, Hibernate also needs to know the fully-qualified name of the entity class via the mapping tag. Once we complete the configuration, we'll use the SessionFactory class, which is the class responsible for creating and pooling JDBC connections.

Typically, this only needs to be set up once for an application:

SessionFactory sessionFactory;
// configures settings from hibernate.cfg.xml 
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure().build(); 
try {
    sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory(); 
} catch (Exception e) {
    // handle the exception
}

Now that we have our connection set up, we can run a query to select all the people from our person table:

Session session = sessionFactory.openSession();
session.beginTransaction();

List<Person> result = session.createQuery("from Person", Person.class).list();
        
result.forEach(person -> {
    //do something with Person instance...   
});
        
session.getTransaction().commit();
session.close();

4.2. MyBatis

MyBatis was introduced in 2010 and is a SQL mapper framework with simplicity as its strength. In another tutorial, we talked about how to integrate MyBatis with Spring and Spring Boot. Here, we'll focus on how to configure MyBatis directly.

To use it, we need to add the mybatis dependency:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.3</version>
</dependency>

Assuming that we reuse the Person class above without annotations, we can proceed to create a PersonMapper interface:

public interface PersonMapper {
    String selectAll = "SELECT * FROM Person"; 
    
    @Select(selectAll)
    @Results(value = {
       @Result(property = "id", column = "ID"),
       @Result(property = "firstName", column = "FIRST_NAME"),
       @Result(property = "lastName", column = "LAST_NAME")
    })
    List<Person> selectAll();
}

The next step is all about the MyBatis configuration:

Configuration initMybatis() throws SQLException {
    DataSource dataSource = getDataSource();
    TransactionFactory trxFactory = new JdbcTransactionFactory();
    
    Environment env = new Environment("dev", trxFactory, dataSource);
    Configuration config = new Configuration(env);
    TypeAliasRegistry aliases = config.getTypeAliasRegistry();
    aliases.registerAlias("person", Person.class);

    config.addMapper(PersonMapper.class);
    return config;
}

DataSource getDataSource() throws SQLException {
    MysqlDataSource dataSource = new MysqlDataSource();
    dataSource.setDatabaseName("test");
    dataSource.setServerName("localhost");
    dataSource.setPort(3306);
    dataSource.setUser("username");
    dataSource.setPassword("password");
    dataSource.setServerTimezone("UTC");
    
    return dataSource;
}

The configuration consists of creating a Configuration object which is a container for settings such as the Environment. It also contains the data source settings.

We can then use the Configuration object, which is normally set up once for an application to create a SqlSessionFactory:

Configuration configuration = initMybatis();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
try (SqlSession session = sqlSessionFactory.openSession()) {
    PersonMapper mapper = session.getMapper(PersonMapper.class);
    List<Person> persons = mapper.selectAll();
    
    // do something with persons list ...
}

4.3. Apache Cayenne

Apache Cayenne is a persistence framework whose first release dates back to 2002. To learn more about it, we suggest reading our introduction to Apache Cayenne.

As usual, let's add the cayenne-server Maven dependency:

<dependency>
    <groupId>org.apache.cayenne</groupId>
    <artifactId>cayenne-server</artifactId>
    <version>4.0.2</version>
</dependency>

We're going to specifically focus on the MySQL connection settings. In this case, we'll configure the cayenne-project.xml:

<?xml version="1.0" encoding="utf-8"?>
<domain project-version="9"> 
    <map name="datamap"/> 
	<node name="datanode" 
	    factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory" 
		schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy"> 
	    <map-ref name="datamap"/> 
		<data-source>
		    <driver value="com.mysql.cj.jdbc.Driver"/> 
			<url value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/> 
			<connectionPool min="1" max="1"/> 
			<login userName="username" password="password"/> 
		</data-source> 
	</node> 
</domain>

After the automatic generation of the datamap.map.xml and Person class in the form of a CayenneDataObject, we can execute some queries.

For example, we'll continue as previously with a select all:

ServerRuntime cayenneRuntime = ServerRuntime.builder()
    .addConfig("cayenne-project.xml")
    .build();

ObjectContext context = cayenneRuntime.newContext();
List<Person> persons = ObjectSelect.query(Person.class).select(context);

// do something with persons list...

5. Connecting Using Spring Data

Spring Data is a Spring-based programming model for data access. Technically, Spring Data is an umbrella project which contains many subprojects that are specific to a given database.

Let's see how to use two of these projects to connect to a MySQL database.

5.1. Spring Data / JPA

Spring Data JPA is a robust framework that helps reduce boilerplate code and provides a mechanism for implementing basic CRUD operations via one of several predefined repository interfaces. In addition to this, it has many other useful features.

Be sure to check out our introduction to Spring Data JPA to learn more.

The spring-data-jpa artifact can be found on Maven Central:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>2.2.4.RELEASE</version>
</dependency>

We'll continue using the Person class. The next step is to configure JPA using annotations:

@Configuration
@EnableJpaRepositories("packages.to.scan")
public class JpaConfiguration {
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
        dataSource.setUsername( "username" );
        dataSource.setPassword( "password" );
        return dataSource;
    }

    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
      return new JpaTransactionManager(emf);
    }

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
      HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
      jpaVendorAdapter.setDatabase(Database.MYSQL);
      jpaVendorAdapter.setGenerateDdl(true);
      return jpaVendorAdapter;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
      LocalContainerEntityManagerFactoryBean lemfb = new LocalContainerEntityManagerFactoryBean();
      lemfb.setDataSource(dataSource());
      lemfb.setJpaVendorAdapter(jpaVendorAdapter());
      lemfb.setPackagesToScan("packages.containing.entity.classes");
      return lemfb;
    }
}

To allow Spring Data to implement the CRUD operations, we have to create an interface that extends the CrudRepository interface:

@Repository
public interface PersonRepository extends CrudRepository<Person, Long> {

}

And finally, let's see an example of select-all with Spring Data:

personRepository.findAll().forEach(person -> {
    // do something with the extracted person
});

5.2. Spring Data / JDBC

Spring Data JDBC is a limited implementation of the Spring Data family, with its primary goal to allow simple access to relational databases.

For this reason, it doesn't provide features like caching, dirty tracking, lazy loading, and many other JPA features.

This time the Maven dependency we need is spring-data-jdbc:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jdbc</artifactId>
    <version>1.1.4.RELEASE</version>
</dependency>

The configuration is lighter compared to the one we used in the previous section for Spring Data JPA:

@Configuration
@EnableJdbcRepositories("packages.to.scan")
public class JdbcConfiguration extends AbstractJdbcConfiguration {
    // NamedParameterJdbcOperations is used internally to submit SQL statements to the database
    @Bean
    NamedParameterJdbcOperations operations() {
        return new NamedParameterJdbcTemplate(dataSource());
    }

    @Bean
    PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        return dataSource;
    }
}

In the case of Spring Data JDBC, we have to define a new Person class or modify the existing one to add some Spring specific annotations.

This is because Spring Data JDBC will take care directly of the entity mapping instead of Hibernate:

import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

@Table(value = "Person")
public class Person {
    @Id
    Long id;

    @Column(value = "FIRST_NAME")
    String firstName;

    @Column(value = "LAST_NAME")
    String lastName;

    // getters and setters
}

With Spring Data JDBC, we can also use the CrudRepository interface. So the declaration will be identical to the one we wrote above in the Spring Data JPA example. Likewise, the same applies to the select-all example.

6. Conclusion

In this tutorial, we have seen several different ways to connect to a MySQL database from Java. We started with the essential JDBC connection. Then we looked at commonly used ORMs like Hibernate, Mybatis, and Apache Cayenne. Finally, we took a look at Spring Data JPA and Spring Data JDBC.

Using JDBC or Hibernate APIs means more boilerplate code. Using robust frameworks, such as Spring Data or Mybatis, require more configuration but give a significant advantage because they provide default implementations and features like caching and lazy loading.

Persistence bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!