Learn to connect to an in-memory database (such as H2 or Hsqldb) from the JUnit 5 unit tests. This will help in writing tests that do not depend on a live database connection.
1.1. Maven Dependency
For demo purposes, we are using the H2 database. You can choose another database of your choice.
Start with importing the latest version of Hsqldb dependency in the application.
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.6.1</version>
<scope>test</scope>
</dependency>
To use the H2 database, use the following dependency.
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>[2.1.212,)</version>
<scope>test</scope>
</dependency>
If not already imported, import the JUnit 5 dependencies.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.1</version>
<scope>test</scope>
</dependency>
2. Setting Up DB Connection Configuration
Now we need to provide the test-specific database connection configuration that will be used by Hibernate in test execution. The config file name we are using is hibernate-test.cfg.xml.
We need to place this file in /test/resources
folder.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbc.JDBCDriver</property>
<property name="hibernate.connection.url">jdbc:hsqldb:mem:test</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>
<property name="hibernate.current_session_context_class">thread</property>
</session-factory>
</hibernate-configuration>
For H2, there will be a small change in the connection properties.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
<property name="hibernate.connection.url">jdbc:h2:mem:test</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>
<property name="hibernate.current_session_context_class">thread</property>
</session-factory>
</hibernate-configuration>
3. Initializing SessionFactory and Session
It’s time to write and test our configuration.
- We will initialize the SessionFactory in @BeforeAll annotated method so that it is initialized once per test class.
- We will initialize the Session in @BeforeEach annotated method so a new session is created for every test.
- We will add only those entities in the metadata that are required in the tests in a specific test class. For example, in this demo, we are adding EmployeeEntity because we only need this entity in the test.
- We will commit the transaction in @AfterEach method and, finally, close the session factory in @AfterAll method.
Note that we have configured the hibernate-test.cfg.xml file in the StandardServiceRegistry. If we do not specify its name, the configuration will, by default, load the src/main/resources/hibernate.cfg.xml file.
public class HelloTest {
private static SessionFactory sessionFactory = null;
private Session session = null;
@BeforeAll
static void setup(){
try {
StandardServiceRegistry standardRegistry
= new StandardServiceRegistryBuilder()
.configure("hibernate-test.cfg.xml")
.build();
Metadata metadata = new MetadataSources(standardRegistry)
.addAnnotatedClass(EmployeeEntity.class)
.getMetadataBuilder()
.build();
sessionFactory = metadata
.getSessionFactoryBuilder().build();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
@BeforeEach
void setupThis(){
session = sessionFactory.openSession();
session.beginTransaction();
}
@AfterEach
void tearThis(){
session.getTransaction().commit();
}
@AfterAll
static void tear(){
sessionFactory.close();
}
@Test
void createSessionFactoryWithXML() {
EmployeeEntity emp = new EmployeeEntity();
emp.setEmail("demo-user@mail.com");
emp.setFirstName("demo");
emp.setLastName("user");
Assertions.assertNull(emp.getEmployeeId());
session.persist(emp);
Assertions.assertNotNull(emp.getEmployeeId());
}
}
For reference, the EmployeeEntity class is:
@Entity
@Table(name = "Employee", uniqueConstraints = {
@UniqueConstraint(columnNames = "ID"),
@UniqueConstraint(columnNames = "EMAIL") })
public class EmployeeEntity implements Serializable {
@Serial
private static final long serialVersionUID = -1798070786993154676L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Integer employeeId;
@Column(name = "EMAIL", unique = true, nullable = false, length = 100)
private String email;
@Column(name = "FIRST_NAME", nullable = false, length = 100)
private String firstName;
@Column(name = "LAST_NAME", nullable = false, length = 100)
private String lastName;
//Getters and Setters are hidden for brevity
}
Now test the above code.
4. Demo
Execute the JUnit test in an IDE or using mvn clean test
command. It will execute the tests and produce the output in the console.
Notice the logs that will have similar information.
5.8.1HHH10001005: Loaded JDBC driver class: org.h2.Driver
5.8.1HHH10001012: Connecting with JDBC URL [jdbc:h2:mem:test]
5.8.1HHH10001001: Connection properties: {password=****, user=sa}
5.8.1HHH10001003: Autocommit mode: false
5.8.1HHH10001115: Connection pool size: 20 (min=1)
HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate:
drop table if exists Employee cascade
Hibernate:
create table Employee (
ID integer generated by default as identity,
EMAIL varchar(100) not null,
FIRST_NAME varchar(100) not null,
LAST_NAME varchar(100) not null,
primary key (ID)
)
Hibernate:
alter table if exists Employee
add constraint UK_ardf0f11mfa6tujs3hflthwdv unique (EMAIL)
Hibernate:
insert
into
Employee
(ID, EMAIL, FIRST_NAME, LAST_NAME)
values
(default, ?, ?, ?)
Hibernate:
drop table if exists Employee cascade
HHH10001008: Cleaning up connection pool [jdbc:h2:mem:test]
Process finished with exit code 0
That’s all for this quick tutorial about Using In-memory Database With Hibernate for Unit Testing.
Happy Learning !!
I believe you may have an erroneous “1” in the password
hibernate.connection.password”>1
When I removed it, it worked.
Thanks none-the-less!
Thank you DtothekK, I faced the same problem 🙂
here is an error in hibernate.cfg.xml
org.hsqldb.jdbcDriver
The name of class is supposed to start with capital later
Code provided is correct. Please refer to : http://hsqldb.org/doc/guide/
Hello Lokesh,
I am wondering why select query is not working in my case.
I mean when i write code to select all data from the data base or say row,It is just executing the select query but list size is 0.why so? your example is show only insert operation.please give me brief about this to how to do it.
You must do insert and select.. both in single session. Please confirm if you are doing it in single session.
Thanks Lokesh, Its very helpful. I just came to know that, we can use hibernate to connect IM database like any other RDBMS by this tutorial.
I have a couple of questions here.
#1. Do we need to install HSql DB separetly? How ? (Some info links would be helpful :))
#2. If i want to use this HSQL data base(or any other IM db) for my Junits, Do i need to dump all my database objects and testdata into this database? Or any other way i can use existing DB as IM DB?
Why I am asking this is, we are working in a project where most of the business logic reside at DB Procedures. I am thinking in a way how can we make our junits without depends on our database(Oracle).
Thanks in Advance. Please Help.
1) For in memory usage, you do not need to install HSQLDB. Just include its jar file as I have added through maven.
2) I guess you will need to dump all your data before execution. It’s in-memory so once it’s not in use, it will be destroyed including DB schema.
If you not want to use external database, then recommended practice is to use mocking.
Thanks again lokesh.
Do u have any idea, what are all other IM databases which is supporting by hibernate4?
Help me with some tutorial links to explore on Hsql db.
There is one more database H2 database, I know of. Try googling for hsqldb related content, because that’s what I will do as well 🙂
Thanks for very helpful tutorial. I’m new in Java and Hibernate as well. I managed to compile the code and understood the concept as well. But the problem is that I don’t know how to execute the code to debug some stuff. When I start app as java application in eclipse. I have a long list to of option. But I don’t see my actual test class to run. So How to run it and debug this app? Please see the link I asked same question in the stackoverflow @ https://stackoverflow.com/questions/26563177/using-in-memory-database-with-hibernate-tutorial-how-to-execute
As this was example only so I was executing it directly from main() method in TestHibernate.java.
hi guys,
i am using Hibernate 4.3.5
i followed the tutorial,
i can see on the logs that the tables are created
but i am having this error on the first insert.
“java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: ADVISORID
did anyone faced the same error ?
thanks
Tried this solution?
thank you for the reply,
in fact , the problem was a definition for a certain column on my entity
thank you for this great tuto, helped a lot.