In the last article we looked at Spring-boot and accessed the database using the JdbcTemplate class.Today we’ll rewrite the same application but by using JPA and Hibernate instead. The Spring Initializr web page have changed a little since the last time, so the first part will be a little different.
- Part 1: Install/setup Oracle database (in docker)
- Part 2: Installing Java (JDK), Eclipse and Maven
- Part 3: Git, Oracle schemas and your first Java application
- Part 4: Your first JDBC Application
- Part 5: Spring-Boot, JdbcTemplate & DB migration (Using FlywayDB)
- Part 6: Spring-boot, JPA and Hibernate (this)
Part 6: Spring-boot, JPA and Hibernate
Disclaimer
First of all, this is not a tutorial on JPA and Hibernate. If you are looking for such a tutorial, you can go to one of the many tutorials out there (for instance “Tutorialpoint – Hibernate” or “Vlad Mihalcea“. This blog is a part of a blog series showing different ways Java Developers work towards an Oracle database. Today we are showing the use of JPA and Hibernate.
I used to hate Hibernate. Why show you?
I have been hired into a lot of projects with performance issues caused by Hibernate. Well – for many years, I thought Hibernate was the big evil. Since then I have learned that this is not really the case. It’s the developers using Hibernate, either the wrong way or for the wrong problems. I have also seen many projects having a lot of success with Hibernate.
In DBA scenarios, such as Oracle User Group conferences, I often hear people laugh whenever Hibernate is mentioned, or people saying “you should never use Hibernate”. I think this is completely wrong, and only makes the distance between DBAs and Java developers bigger.
So in this article we’ll have our first look at this “awful” tool that very many Java developers use when writing code towards relational databases.
What is JPA?
JPA (or Java Persistence API) is just a specification, and consist of only interfaces. So it is not a product in it self, and requires an implementation. The JPA specification describes a way of accessing, persisting, and managing data between Java objects and relational databases. One of these implementations are found in Hibernate, which we’ll have a look at below.
What is Hibernate
Hibernate is an ORM API, used when writing Java code to map between Objects in the code and relations in an relational database. Hibernate provides both an proprietary POJO API and an implementation of the JPA specification. So Hibernate can be used without JPA, and JPA can be used with another implementation such as TopLink.
The todays demo application: SpringHibernateDemo
Spring Initializr
We’ll start up by using the Spring Initializr at https://start.spring.io/. Choose the following settings:
Then add the dependencies by using the search functionality. Write “web” in the “Search dependencies to add”. Below you should now see different libraries showing up. Click the plus sign on the “Web” library:
Do the same for “JDBC” and “JPA”. You should then end up with the following dependencies:
Note! You could also add “Flyway” the same way as “JDBC” and “JPA” (shown above). Instead we’ll add this manually in the pom.xml file in the next section.
Click the “Generate Project” button, and save the “spring-hibernate.zip” file. Unzip the file in a suitable location, and open project in Eclipse (File -> “Open Project from File System …”).
Adding Oracle JDBC and Flyway dependencies
Add the Oracle JDBC and Flyway dependencies to the pom.xml:
<dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> <dependency> <groupId>com.oracle.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>12.2.0.1</version> </dependency>
And then the Flyway build dependencies and configuration:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.flywaydb</groupId> <artifactId>flyway-maven-plugin</artifactId> <configuration> <url>jdbc:oracle:thin:@localhost:152/ORCL</url> <user>demo</user> <password>demo</password> <locations>db/migration/</locations> <table>SCHEMA_VERSION</table> </configuration> <dependencies> <dependency> <groupId>com.oracle.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>12.2.0.1</version> </dependency> </dependencies> </plugin> </plugins> </build>
Copy the Flyway DB script
We’ll use the same EMP table as in part 5. Copy the Flyway script (V1__table_emp.sql) from “src/main/resources/db/migration” (in spring-jdbc project) to a similar directory in the spring-hibernate project. After I wrote part 5 I have had some bad experience with IDENTITY columns and the auto-generated sequence. If you delete the table, you won’t be able to delete the sequence (as I know). So I’ll change this to the good-old sequence, but with a default value:
create sequence emp_seq start with 1 increment by 1 cache 100; create table emp( empno number(4,0) DEFAULT emp_seq.nextval, ename varchar2(10), job varchar2(9), mgr number(4,0), hiredate date, sal number(10,2), comm number(7,2), deptno number(2,0), constraint pk_emp primary key (empno) ); insert into emp (ename, job, mgr, hiredate, sal, comm, deptno) values ('LARRY', 'CEO', null, sysdate-4000, 10000, null, 10); insert into emp (ename, job, mgr, hiredate, sal, comm, deptno) values ('KING', 'Sales', 1000, sysdate-3000, 5000, null, 30); commit;
Test the flyway setup by using the maven command line utility:
Lasses-MacBook-Pro-5:~ lassejenssen$ cd WS/spring-hibernate Lasses-MacBook-Pro-5:spring-hibernate lassejenssen$ &lt;strong&gt;mvn flyway:clean&lt;/strong&gt; [INFO] ------------------&lt; no.eritec.demo:spring-hibernate &gt;------------------- [INFO] Building spring-hibernate 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- flyway-maven-plugin:5.2.4:clean (default-cli) @ spring-hibernate --- [INFO] Flyway Community Edition 5.2.4 by Boxfuse [INFO] Database: jdbc:oracle:thin:@localhost:152/ORCL (Oracle 12.2) [INFO] Successfully cleaned schema "DEMO" (execution time 00:01.998s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.666 s [INFO] Finished at: 2019-03-18T17:34:16+01:00 [INFO] ------------------------------------------------------------------------ Lasses-MacBook-Pro-5:spring-hibernate lassejenssen$ &lt;strong&gt;mvn flyway:migrate&lt;/strong&gt; [INFO] Scanning for projects... [INFO] [INFO] ------------------&lt; no.eritec.demo:spring-hibernate &gt;------------------- [INFO] Building spring-hibernate 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- flyway-maven-plugin:5.2.4:migrate (default-cli) @ spring-hibernate --- [INFO] Flyway Community Edition 5.2.4 by Boxfuse [INFO] Database: jdbc:oracle:thin:@localhost:152/ORCL (Oracle 12.2) [INFO] Successfully validated 1 migration (execution time 00:00.018s) [INFO] Creating Schema History table: "DEMO"."SCHEMA_VERSION" [INFO] Current version of schema "DEMO": &lt;&lt; Empty Schema &gt;&gt; [INFO] Migrating schema "DEMO" to version 1 - table emp [INFO] Successfully applied 1 migration to schema "DEMO" (execution time 00:00.205s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.373 s [INFO] Finished at: 2019-03-18T17:34:29+01:00 [INFO] ------------------------------------------------------------------------ Lasses-MacBook-Pro-5:spring-hibernate lassejenssen$
Adding Entity class: Employee
Now you should be comfortable at creating a new package. Create the “no.eritec.demo.springhibernate.entity” package, and copy the Employee.java file from your project from part 5 (from the domain package). Change the import of “java.sql.Date” to “java.util.Date”:
package no.eritec.demo.springhibernate.entity; import java.util.Date; public class Employee { private int empno; private String ename; private String job; private int managerNo; private Date hiredate; private int salary; private int comm; private int deptno; public Employee() { super(); } public Employee( int empno, String ename, String job, int managerNo, int salary, int comm, int deptno) { this.empno = empno; this.ename = ename; this.job = job; this.managerNo = managerNo; this.salary = salary; this.comm = comm; this.deptno = deptno; } public Date getHiredate() { return hiredate; } public void setHiredate(Date hiredate) { this.hiredate = hiredate; } public int getEmpno() { return empno; } public void setEmpno(int empno) { this.empno = empno; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public int getManagerNo() { return managerNo; } public void setManagerNo(int managerNo) { this.managerNo = managerNo; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public int getComm() { return comm; } public void setComm(int comm) { this.comm = comm; } public int getDeptno() { return deptno; } public void setDeptno(int deptno) { this.deptno = deptno; } }
JPA annotations
Now we’ll start adding JPA annotations. First we tell Spring that this class is an @Entity. We also map the Employee class to the EMP table in the database by using the @Table annotation:
import javax.persistence.Entity; import javax.persistence.Table; @Entity @Table(name="EMP") public class Employee { ...
Then we add the @Id annotation for the primary key column, and use the @GeneratedValue to tell Spring that this value will be generated automatically by Oracle (because of the default value and sequence). We also use the @Column annotation to map columns with a different name in the entity class (than in the database). At last I change the datatypes for those columns which are nullable away from primitive datatypes.
The complete Employee class:
package no.eritec.demo.springhibernate.entity; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="EMP") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "EMP_SEQ") private int empno; private String ename; private String job; @Column(name="mgr") private Integer managerNo; private Date hiredate; @Column(name="sal") private Double salary; private Integer comm; private Integer deptno; public Employee() { super(); } public Employee( int empno, String ename, String job, int managerNo, int salary, int comm, int deptno) { this.empno = empno; this.ename = ename; this.job = job; this.managerNo = managerNo; this.salary = salary; this.comm = comm; this.deptno = deptno; } public Date getHiredate() { return hiredate; } public void setHiredate(Date hiredate) { this.hiredate = hiredate; } public int getEmpno() { return empno; } public void setEmpno(int empno) { this.empno = empno; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public int getManagerNo() { return managerNo; } public void setManagerNo(int managerNo) { this.managerNo = managerNo; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public int getComm() { return comm; } public void setComm(int comm) { this.comm = comm; } public int getDeptno() { return deptno; } public void setDeptno(int deptno) { this.deptno = deptno; } }
Creating a database repository class
Now we’ll be writing the code accessing the EMP table in the database. Or actually, we’ll let Spring create it for us. The only thing we need to do is to create a repository interface extending the JpaRepository class.
Start by adding a new package: “no.eritec.demo.springhibernate.repository”.
In the new package create a new interface: “EmployeeRepository”:
package no.eritec.demo.springhibernate.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import no.eritec.demo.springhibernate.entity.Employee; @Repository public interface EmployeeRepository extends JpaRepository<Employee, Integer> { }
Adding the Spring database connection information
Now we just need to configure the application to which database and schema to connect to when running. We also add configuration which tell the JPA implementation to show the SQLs ran in the log. We add all this in the application.properties file:
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/orcl spring.datasource.username=demo spring.datasource.password=demo # Show all queries spring.jpa.show-sql=true
Writing the test class (SpringHibernateApplicationTest)
Then we are ready to write some test code in the test class SpringHibernateApplicationTest (found under “/src/test/java”). I start by adding a Logger variable, so I can write to the log output.
I create three test methods: One for adding a new Employee (test1AddEmployee), one for listing all the employees (test2ShowEmployees), and one counting the employees (test3CountEmployees). I start every method by “testN” (where N is an increasing integer) and add the annotation @FixMethodOrder(MethodSorters.NAME_ASCENDING) to the class. Now the test methods will run in the order they are listed:
package no.eritec.demo.springhibernate; import java.util.Date; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import no.eritec.demo.JavaOugnDemo.JavaOugnDemoApplicationTests; import no.eritec.demo.springhibernate.entity.Employee; import no.eritec.demo.springhibernate.repository.EmployeeRepository; @RunWith(SpringRunner.class) @SpringBootTest @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SpringHibernateApplicationTests { Logger log = LoggerFactory.getLogger(SpringHibernateApplicationTests.class); @Autowired EmployeeRepository repo; @Test public void contextLoads() { } @Test public void test1AddEmployee() { Employee emp = new Employee(); emp.setEname("HARRY"); emp.setDeptno(10); emp.setHiredate(new Date()); emp.setJob("Sales"); Employee emp2 = repo.save(emp); assertNotNull(emp2.getEmpno()); } @Test public void test2ShowEmployees() { log.info("Employees:"); log.info("========================="); Listlist = repo.findAll(); for (Employee e:list) { log.info(e.getEname()); } assertEquals(list.size(),4); } @Test public void test3CountEmployees() { int cnt = (int) repo.count(); log.info("Count: " + cnt); assertEquals(cnt,4); } }
Now you can click the “Run” button to run your tests. Note! You might need to do a “mvn flyway:clean” first. You could also run this using maven from command line:
Lasses-MacBook-Pro-5:spring-hibernate lassejenssen$ mvn flyway:clean [INFO] Scanning for projects... [INFO] [INFO] ------------------< no.eritec.demo:spring-hibernate >------------------- [INFO] Building spring-hibernate 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [WARNING] The POM for javax.xml.bind:jaxb-api:jar:2.3.1 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details [INFO] [INFO] --- flyway-maven-plugin:5.2.4:clean (default-cli) @ spring-hibernate --- [INFO] Flyway Community Edition 5.2.4 by Boxfuse [INFO] Database: jdbc:oracle:thin:@localhost:152/ORCL (Oracle 12.2) [INFO] Successfully cleaned schema "DEMO" (execution time 00:00.255s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.105 s [INFO] Finished at: 2019-03-18T20:11:39+01:00 [INFO] ------------------------------------------------------------------------ Lasses-MacBook-Pro-5:spring-hibernate lassejenssen$ mvn package [INFO] Scanning for projects... [INFO] [INFO] ------------------< no.eritec.demo:spring-hibernate >------------------- [INFO] Building spring-hibernate 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [WARNING] The POM for javax.xml.bind:jaxb-api:jar:2.3.1 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details [INFO] [INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ spring-hibernate --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 1 resource [INFO] Copying 2 resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ spring-hibernate --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ spring-hibernate --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /Users/lassejenssen/WS/spring-hibernate/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ spring-hibernate --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ spring-hibernate --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running no.eritec.demo.springhibernate.SpringHibernateApplicationTests 20:12:04.218 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.224 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate] 20:12:04.230 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)] 20:12:04.250 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper] 20:12:04.262 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests], using SpringBootContextLoader 20:12:04.265 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests]: class path resource [no/eritec/demo/springhibernate/SpringHibernateApplicationTests-context.xml] does not exist 20:12:04.265 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests]: class path resource [no/eritec/demo/springhibernate/SpringHibernateApplicationTestsContext.groovy] does not exist 20:12:04.265 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}. 20:12:04.266 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests]: SpringHibernateApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration. 20:12:04.309 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.395 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/Users/lassejenssen/WS/spring-hibernate/target/classes/no/eritec/demo/springhibernate/SpringHibernateApplication.class] 20:12:04.397 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration no.eritec.demo.springhibernate.SpringHibernateApplication for test class no.eritec.demo.springhibernate.SpringHibernateApplicationTests 20:12:04.486 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests]: using defaults. 20:12:04.487 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 20:12:04.495 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [javax/servlet/ServletContext] 20:12:04.502 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@384ad17b, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@61862a7f, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@441772e, org.springframework.test.context.support.DirtiesContextTestExecutionListener@7334aada, org.springframework.test.context.transaction.TransactionalTestExecutionListener@1d9b7cce, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@4d9e68d0, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@42e99e4a, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@14dd9eb7, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@52e6fdee, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@6c80d78a, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@62150f9e] 20:12:04.504 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.504 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.506 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.506 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.507 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.507 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.512 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@7b9a4292 testClass = SpringHibernateApplicationTests, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@4a94ee4 testClass = SpringHibernateApplicationTests, locations = '{}', classes = '{class no.eritec.demo.springhibernate.SpringHibernateApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30a3107a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@52feb982, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@36b4cef0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@45820e51], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null]. 20:12:04.513 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.513 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [no.eritec.demo.springhibernate.SpringHibernateApplicationTests] 20:12:04.518 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@7b9a4292 testClass = SpringHibernateApplicationTests, testInstance = no.eritec.demo.springhibernate.SpringHibernateApplicationTests@5158b42f, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@4a94ee4 testClass = SpringHibernateApplicationTests, locations = '{}', classes = '{class no.eritec.demo.springhibernate.SpringHibernateApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30a3107a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@52feb982, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@36b4cef0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@45820e51], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]]. 20:12:04.541 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=-1} . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.3.RELEASE) 2019-03-18 20:12:04.731 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : Starting SpringHibernateApplicationTests on Lasses-MacBook-Pro-5.local with PID 34704 (started by lassejenssen in /Users/lassejenssen/WS/spring-hibernate) 2019-03-18 20:12:04.732 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : No active profile set, falling back to default profiles: default 2019-03-18 20:12:05.038 INFO 34704 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode. 2019-03-18 20:12:05.093 INFO 34704 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 49ms. Found 1 repository interfaces. 2019-03-18 20:12:05.616 INFO 34704 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 5.2.4 by Boxfuse 2019-03-18 20:12:05.625 INFO 34704 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2019-03-18 20:12:06.160 INFO 34704 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2019-03-18 20:12:06.163 INFO 34704 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:oracle:thin:@//localhost:152/orcl (Oracle 12.2) 2019-03-18 20:12:06.394 INFO 34704 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 2 migrations (execution time 00:00.019s) 2019-03-18 20:12:06.495 INFO 34704 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table: "DEMO"."flyway_schema_history" 2019-03-18 20:12:06.661 INFO 34704 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema "DEMO": << Empty Schema >> 2019-03-18 20:12:06.662 INFO 34704 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "DEMO" to version 1 - table emp 2019-03-18 20:12:06.808 INFO 34704 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "DEMO" to version 2 - table dept 2019-03-18 20:12:06.855 INFO 34704 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 2 migrations to schema "DEMO" (execution time 00:00.362s) 2019-03-18 20:12:07.058 INFO 34704 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [ name: default ...] 2019-03-18 20:12:07.218 INFO 34704 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.7.Final} 2019-03-18 20:12:07.220 INFO 34704 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found 2019-03-18 20:12:07.344 INFO 34704 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final} 2019-03-18 20:12:07.471 INFO 34704 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.Oracle12cDialect 2019-03-18 20:12:07.538 INFO 34704 --- [ main] org.hibernate.type.BasicTypeRegistry : HHH000270: Type registration [byte[]] overrides previous : org.hibernate.type.BinaryType@34585ac9 2019-03-18 20:12:07.538 INFO 34704 --- [ main] org.hibernate.type.BasicTypeRegistry : HHH000270: Type registration [[B] overrides previous : org.hibernate.type.BinaryType@34585ac9 2019-03-18 20:12:07.539 INFO 34704 --- [ main] org.hibernate.type.BasicTypeRegistry : HHH000270: Type registration [Byte[]] overrides previous : org.hibernate.type.WrapperBinaryType@167381c7 2019-03-18 20:12:07.539 INFO 34704 --- [ main] org.hibernate.type.BasicTypeRegistry : HHH000270: Type registration [[Ljava.lang.Byte;] overrides previous : org.hibernate.type.WrapperBinaryType@167381c7 2019-03-18 20:12:07.988 INFO 34704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2019-03-18 20:12:08.445 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : Started SpringHibernateApplicationTests in 3.902 seconds (JVM running for 4.58) Hibernate: select emp_seq.nextval from dual Hibernate: insert into emp (comm, deptno, ename, hiredate, job, mgr, sal, empno) values (?, ?, ?, ?, ?, ?, ?, ?) 2019-03-18 20:12:08.594 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : Employees: 2019-03-18 20:12:08.594 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : ========================= 2019-03-18 20:12:08.635 INFO 34704 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory Hibernate: select employee0_.empno as empno1_0_, employee0_.comm as comm2_0_, employee0_.deptno as deptno3_0_, employee0_.ename as ename4_0_, employee0_.hiredate as hiredate5_0_, employee0_.job as job6_0_, employee0_.mgr as mgr7_0_, employee0_.sal as sal8_0_ from emp employee0_ 2019-03-18 20:12:08.759 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : LARRY 2019-03-18 20:12:08.759 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : KING 2019-03-18 20:12:08.759 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : LINDA 2019-03-18 20:12:08.759 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : HARRY Hibernate: select count(*) as col_0_0_ from emp employee0_ 2019-03-18 20:12:08.780 INFO 34704 --- [ main] n.e.d.s.SpringHibernateApplicationTests : Count: 4 [INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.688 s - in no.eritec.demo.springhibernate.SpringHibernateApplicationTests 2019-03-18 20:12:08.819 INFO 34704 --- [ Thread-4] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2019-03-18 20:12:08.822 INFO 34704 --- [ Thread-4] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2019-03-18 20:12:08.882 INFO 34704 --- [ Thread-4] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. [INFO] [INFO] Results: [INFO] [INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ spring-hibernate --- [INFO] Building jar: /Users/lassejenssen/WS/spring-hibernate/target/spring-hibernate-0.0.1-SNAPSHOT.jar [INFO] [INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:repackage (repackage) @ spring-hibernate --- [INFO] Replacing main artifact with repackaged archive [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.883 s [INFO] Finished at: 2019-03-18T20:12:10+01:00 [INFO] ------------------------------------------------------------------------
Customizing the EmployeeRepository
In addition to all the methods inherited from the JpaRepository class, I would also like to have a method that let me search by name. Then I extend my EmployeeRepository interface, and add another test method to my test class.
New version of EmployeeRepository.java:
@Repository public interface EmployeeRepository extends JpaRepository<Employee, Integer>{ @Query("SELECT e FROM Employee e WHERE e.ename like :eName") List findByName(@Param("eName") String ename); }
New test method:
@Test public void test4SearchByName() { List list = repo.findByName("L%"); int cnt = list.size(); log.info("Count employees starting with 'L': " + cnt); assertEquals(cnt,2); }
And then you can run the “mvn flyway:clean” and “mvn package” again.
Running Flyway clean from code
If you don’t want to run “mvn flyway:clean” before running your test, you can run this from your test class.
Under I have added code to make Flyway do a “clean” and a “migrate” in the contextLoads method:
@RunWith(SpringRunner.class) @SpringBootTest @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SpringHibernateApplicationTests { Logger log = LoggerFactory.getLogger(SpringHibernateApplicationTests.class); @Autowired EmployeeRepository repo; @Autowired DataSource dataSource; @Test public void contextLoads() { final Flyway flyway = new Flyway(); flyway.setDataSource(dataSource); log.info("FLYWAY: Starting clean"); flyway.clean(); log.info("FLYWAY: Starting migration"); flyway.migrate(); log.info("FLYWAY: Migration Complete"); } ... }
This gives the following output when running the test:
2019-03-19 10:32:02.651 INFO 43287 --- [ main] n.e.d.s.SpringHibernateApplicationTests : FLYWAY: Starting clean 2019-03-19 10:32:02.653 INFO 43287 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:oracle:thin:@//localhost:152/orcl (Oracle 12.2) 2019-03-19 10:32:03.606 INFO 43287 --- [ main] o.f.core.internal.command.DbClean : Successfully cleaned schema "DEMO" (execution time 00:00.928s) 2019-03-19 10:32:03.608 INFO 43287 --- [ main] n.e.d.s.SpringHibernateApplicationTests : FLYWAY: Starting migration 2019-03-19 10:32:03.631 INFO 43287 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 2 migrations (execution time 00:00.006s) 2019-03-19 10:32:03.713 INFO 43287 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table: "DEMO"."flyway_schema_history" 2019-03-19 10:32:03.871 INFO 43287 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema "DEMO": << Empty Schema >> 2019-03-19 10:32:03.872 INFO 43287 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "DEMO" to version 1 - table emp 2019-03-19 10:32:04.003 INFO 43287 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "DEMO" to version 2 - table dept 2019-03-19 10:32:04.045 INFO 43287 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 2 migrations to schema "DEMO" (execution time 00:00.335s) 2019-03-19 10:32:04.050 INFO 43287 --- [ main] n.e.d.s.SpringHibernateApplicationTests : FLYWAY: Migration Complete
Post a Comment