Spring Boot & JPA & Hibernate & Oracle
In this tutorial we show how to create a Spring Boot application that communicates with an Oracle data source through Hibernate.
Prerequisites:
- Eclipse IDE (neon release)
- Maven 4
- Java 1.8
1- Create maven project
Open eclipse, then create a new maven project and name it as SpringBootHibernate.
At the end of this tutorial, we’ll get the following project structure:
2- pom.xml
Configure Spring Boot inside pom.xml through adding the following parent dependency:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> </parent>
Then add spring-boot-starter dependency in order to run our application as a standalone jar application:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
Now in order to make use of Spring data jpa and hibernate, we need to just add spring-boot-starter-data-jpa as a dependency:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
As soon as we include Spring Boot Starter JPA in our project, we get the following features from a wide variety of dependencies:
- Auto-configuration of an in-memory embedded database which allows you to run your application without even setting up a database.
- Auto-import of JPA API and Hibernate, adding this dependency will automatically import JPA API and use Hibernate as the default implementation.
- Auto-read of the data source and hibernate configuration from application.properties.
- Auto-creation of the entities as tables and auto execution of import.sql.
This is the whole pom.xml for reference:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.programmer.gate</groupId> <artifactId>SpringBootHibernate</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <name>SpringBootHibernate</name> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3- Add oracle driver to classpath
In this tutorial, we’re going to override the default in-memory database provided by Spring Boot and use our own oracle database.
For this purpose we add “oracle-ojdbc6-11.2.0.3.jar” under WEB-INF/lib and define it in our classpath.
4- application.properties
Configure oracle data source and hibernate in application.properties:
# create and drop tables and sequences, loads import.sql spring.jpa.hibernate.ddl-auto=create-drop # Oracle settings spring.datasource.url=jdbc:oracle:thin:@localhost:1522:orcl spring.datasource.username=HIBERNATE_TEST spring.datasource.password=HIBERNATE_TEST spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver # logging logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n logging.level.org.hibernate.SQL=debug
5- Entities
Our entities represent a player and a team with a one to many relationship, each team could have many players while a player could only play with a single team at a time.
So we create our entities under com.programmer.gate.model package:
Player.java
package com.programmer.gate.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.SequenceGenerator; @Entity public class Player { @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "player_Sequence") @SequenceGenerator(name = "player_Sequence", sequenceName = "PLAYER_SEQ") private Long id; @Column(name = "name") private String name; @Column(name = "num") private int num; @Column(name = "position") private String position; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id", nullable = false) private Team team; public Player() { } // getters/setters }
Team.java
package com.programmer.gate.model; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator; @Entity public class Team { @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "team_Sequence") @SequenceGenerator(name = "team_Sequence", sequenceName = "TEAM_SEQ") private Long id; @Column(name = "name") private String name; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "team") private List<Player> players; public Team() { } // getters/setters }
Since we set spring.jpa.hibernate.ddl-auto=create-drop inside application.properties, our application will automatically create Player and Team entities in our database along with their sequences and constraints.
Our application would also look for import.sql in the classpath and executes it if found.
In our example, we define import.sql under src/main/resources in order to fill our tables with static data:
insert into Team (id,name) values(1,'Barcelona'); insert into Player (id, team_id, name, num, position) values(1,1,'Lionel Messi', 10, 'Forward'); insert into Player (id, team_id, name, num, position) values(2,1,'Andreas Inniesta', 8, 'Midfielder'); insert into Player (id, team_id, name, num, position) values(3,1,'Pique', 3, 'Defender');
6- Repositories
We define our repositories interfaces under com.programmer.gate.repository. Each repository extends Spring CrudRepository which provides a default implementation for the basic find,save and delete methods, so that we don’t care about defining implementation classes for them.
PlayerRepository
package com.programmer.gate.repository; import java.util.List; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import com.programmer.gate.model.Player; @Repository public interface PlayerRepository extends CrudRepository<Player, Long> { List<Player> findByTeamId(long teamId); }
TeamRepository
package com.programmer.gate.repository; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import com.programmer.gate.model.Team; @Repository public interface TeamRepository extends CrudRepository<Team, Long> { Team findByPlayers(long playerId); }
7- Service
Now we define our service class which holds the business logic of our application, our service exposes 2 methods: getAllTeamPlayers() and addBarcelonaPlayer() ( just rename it to your favorite club if you don’t like Barcelona :D), our service layer communicates directly with the repository layer.
SoccerService.java
package com.programmer.gate.service; import java.util.List; public interface SoccerService { public List<String> getAllTeamPlayers(long teamId); public void addBarcelonaPlayer(String name, String position, int number); }
SoccerServiceImpl
package com.programmer.gate.service; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.programmer.gate.model.Player; import com.programmer.gate.model.Team; import com.programmer.gate.repository.PlayerRepository; import com.programmer.gate.repository.TeamRepository; @Service public class SoccerServiceImpl implements SoccerService { @Autowired private PlayerRepository playerRepository; @Autowired private TeamRepository teamRepository; public List<String> getAllTeamPlayers(long teamId) { List<String> result = new ArrayList<String>(); List<Player> players = playerRepository.findByTeamId(teamId); for (Player player : players) { result.add(player.getName()); } return result; } public void addBarcelonaPlayer(String name, String position, int number) { Team barcelona = teamRepository.findOne(1l); Player newPlayer = new Player(); newPlayer.setName(name); newPlayer.setPosition(position); newPlayer.setNum(number); newPlayer.setTeam(barcelona); playerRepository.save(newPlayer); } }
8- Application.java
The final step is to create the Spring Boot initializer, this is the entry point of our application. We define Application.java under com.programmer.gate.
package com.programmer.gate; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.programmer.gate.service.SoccerService; @SpringBootApplication public class Application implements CommandLineRunner{ @Autowired SoccerService soccerService; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void run(String... arg0) throws Exception { soccerService.addBarcelonaPlayer("Xavi Hernandez", "Midfielder", 6); List<String> players = soccerService.getAllTeamPlayers(1); for(String player : players) { System.out.println("Introducing Barca player => " + player); } } }
P.S: it’s worth to mention that Spring Boot application automatically reads and creates entities, repositories and services defined in the same or in a sub-package relative to where you have your initializer class, so if we define Application.java under a different package then we need to explicitly specify the package of model, repository and service.
Output:
When running the application as a standard java application, we get the following output in the console.
2018-04-13 14:54:47 DEBUG org.hibernate.SQL - create sequence player_seq start with 1 increment by 1 2018-04-13 14:54:47 DEBUG org.hibernate.SQL - create sequence team_seq start with 1 increment by 1 2018-04-13 14:54:47 DEBUG org.hibernate.SQL - create table player (id number(19,0) not null, name varchar2(255 char), num number(10,0), position varchar2(255 char), team_id number(19,0) not null, primary key (id)) 2018-04-13 14:54:47 DEBUG org.hibernate.SQL - create table team (id number(19,0) not null, name varchar2(255 char), primary key (id)) 2018-04-13 14:54:47 DEBUG org.hibernate.SQL - alter table player add constraint FKdvd6ljes11r44igawmpm1mc5s foreign key (team_id) references team 2018-04-13 14:54:47 INFO o.h.tool.hbm2ddl.SchemaExport - HHH000476: Executing import script '/import.sql' 2018-04-13 14:54:47 INFO o.h.tool.hbm2ddl.SchemaExport - HHH000230: Schema export complete 2018-04-13 14:54:47 INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default' 2018-04-13 14:54:48 INFO o.s.j.e.a.AnnotationMBeanExporter - Registering beans for JMX exposure on startup 2018-04-13 14:54:48 DEBUG org.hibernate.SQL - select team0_.id as id1_1_0_, team0_.name as name2_1_0_, players1_.team_id as team_id5_0_1_, players1_.id as id1_0_1_, players1_.id as id1_0_2_, players1_.name as name2_0_2_, players1_.num as num3_0_2_, players1_.position as position4_0_2_, players1_.team_id as team_id5_0_2_ from team team0_, player players1_ where team0_.id=players1_.team_id(+) and team0_.id=? 2018-04-13 14:54:48 DEBUG org.hibernate.SQL - select player_seq.nextval from dual 2018-04-13 14:54:48 DEBUG org.hibernate.SQL - insert into player (name, num, position, team_id, id) values (?, ?, ?, ?, ?) 2018-04-13 14:54:48 INFO o.h.h.i.QueryTranslatorFactoryInitiator - HHH000397: Using ASTQueryTranslatorFactory 2018-04-13 14:54:48 DEBUG org.hibernate.SQL - select player0_.id as id1_0_, player0_.name as name2_0_, player0_.num as num3_0_, player0_.position as position4_0_, player0_.team_id as team_id5_0_ from player player0_, team team1_ where player0_.team_id=team1_.id(+) and team1_.id=? Introducing Barca player => Lionel Messi Introducing Barca player => Andreas Inniesta Introducing Barca player => Pique Introducing Barca player => Xavi Hernandez 2018-04-13 14:54:49 INFO com.programmer.gate.Application - Started Application in 4.213 seconds (JVM running for 4.555)
9- Source code
You can download the source code from this repository: spring-boot-jpa-hibernate
Published on Java Code Geeks with permission by Hussein Terek, partner at our JCG program. See the original article here: Spring Boot + JPA + Hibernate + Oracle Opinions expressed by Java Code Geeks contributors are their own. |