Integrate Spring with Hibernate

Today we will discuss how to use Hibernate as a persistence provider with Spring when implementing data access logic with the ORM. Spring provides support to use JPA in Spring applications. If you are not familiar with Hibernate, please go through the Hibernate tutorial first.

Maven Dependencies

We will use the Oracle database in our example. First step of our set-up is to declare the following dependencies: spring-context, spring-data-jpa, hibernate-entitymanager and Oracle driver.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.21</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.6.9.Final</version>
</dependency>
<dependency>
    <groupId>com.oracle.ojdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>19.3.0.0</version>
</dependency>
Property file

Let’s create a property file called DBConfig.properties to hold database connection related configuration and keep it under the src/main/resources folder.

db.driver=oracle.jdbc.driver.OracleDriver
db.url=jdbc:oracle:thin:@localhost:1521:xe
db.user=test
db.password=test
db.hibernate.dialect=org.hibernate.dialect.Oracle9Dialect
db.hibernate.show_sql=true
Configuration Class

The next step is to configure the EntityManagerFactory bean. We will use LocalContainerEntityManagerFactoryBean that supports the injection of DataSource and can participate in both global and local transactions.

@ComponentScan(basePackages = {"com.kcs"})
@PropertySource("classpath:DBConfig.properties")
@EnableTransactionManagement
@Configuration
public class AppConfig {
    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setUrl(env.getProperty("db.url"));
        ds.setUsername(env.getProperty("db.user"));
        ds.setPassword(env.getProperty("db.password"));
        ds.setDriverClassName(env.getProperty("db.driver"));
        return ds;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource());
        emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        emf.setPackagesToScan("com.kcs.entity");
        emf.setJpaProperties(hibernateProps());
        return emf;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

    private Properties hibernateProps() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", env.getProperty("db.hibernate.dialect"));
        properties.setProperty("hibernate.show_sql", env.getProperty("db.hibernate.show_sql"));
        return properties;
    }
}

Please note –

  1. We have created a transaction manager bean (transactionManager) for transactional data access.
  2. To enable annotation-driven transaction management capability, we have annotated our configuration class with @EnableTransactionManagement.
  3. We have created an EntityManagerFactory bean and set a package to scan for entities.
Create a new table in database

We will create a new table in the database as shown below. We will create it under schema demo.

Create corresponding entity
@Entity
@Table(name = "USER_DETAILS", schema = "demo")
public class User {
    @Id
    @Column(name = "USER_ID")
    private long userId;

    @Column(name = "USER_NAME")
    private String userName;
    
    @Column(name = "EMAIL")
    private String email;

    // getters and setters
}
Create JPA repository

In Spring Data JPA, the repository abstraction provides a simple way for JPA-based data access. The main interface in Spring Data is  Repository<T,ID>. Here T indicates the entity type and ID indicates the type of the id of that entity. Different extensions to this interface are provided by Spring. One of them is the JpaRepository which we are going to use.

To enable Spring Data JPA repository, we have to use @EnableJpaRepositories in our configuration class and we can provide the package where the repository classes are present.

So, let’s first update the configuration class.

@ComponentScan(basePackages = {"com.kcs"})
@PropertySource("classpath:DBConfig.properties")
@EnableTransactionManagement
@EnableJpaRepositories("com.kcs.repository")
@Configuration
public class AppConfig {
    ....
}

Now create a user repository interface that will extend the JpaRepository interface.

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("select a from User a where a.userId = :id")
    User findById1(@Param("id") Long userId);

    @Query(value = "SELECT * FROM DEMO.USER_DETAILS WHERE USER_ID = :id", nativeQuery = true)
    User findById2(@Param("id") Long userId);

    List<User> findByUserName(String name);
}

As you can see, we just need to declare the methods in this interface, that’s it. But we should follow the common naming convention. For example, when you try to fetch some data, your method name should have two parts separated by the first By keyword. The first part can be find, get etc. Second part is the criteria part that contains the property name of the entity. We can also use And and Or to include multiple criteria like findByUserNameAndUserId(). Also we can create methods to accept native SQL or JPQL query.

Also, by inheriting from JpaRepository, we are getting many methods without the need to declare them. Few of these methods are –

  • findAll
  • save
  • saveAll
  • delete
  • deleteAll
Create Service

Now create a service class to save and get user data from the database using UserRepository.

@Service
public class UserService {
    @Autowired
    public UserRepository personRepository;
    
    @Transactional
    public User saveUser(User user) {
        return personRepository.save(user);
    }
    
    @Transactional(readOnly=true)
    public User findById1(Long userId) {
        return personRepository.findById1(userId);
    }
    
    @Transactional(readOnly=true)
    public User findById2(Long userId) {
        return personRepository.findById2(userId);
    }

    @Transactional(readOnly=true)
    public List<User> findByUserName(String name) {
        return personRepository.findByUserName(name);
    }
}

@Transactional annotation indicates that the method will be executed inside a Spring managed transaction.

Test the app

Now everything is ready and we can go ahead and test the application. We will write everything inside a main method for simplicity.

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean(UserService.class);

User user = new User();
user.setUserId(1L);
user.setUserName("Tom");
user.setEmail("[email protected]");
		
userService.saveUser(user);
System.out.println(userService.findById1(1L));
System.out.println(userService.findById2(1L));
System.out.println(userService.findByUserName("Tom"));

You will see following output in the console –

insert into demo.USER_DETAILS (EMAIL, USER_NAME, USER_ID) values (?, ?, ?)

select ... from demo.USER_DETAILS user0_ where user0_.USER_ID=?
User [userId=1, userName=Tom, [email protected]]

SELECT * FROM DEMO.USER_DETAILS WHERE USER_ID = ?
User [userId=1, userName=Tom, [email protected]]

select ... from demo.USER_DETAILS user0_ where user0_.USER_NAME=?
[User [userId=1, userName=Tom, [email protected]]]

You see, everything worked just fine and the best part is for database interaction we had to write very little code.

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.