h1(#how-to-create-a-dao-using-jpa). How to create a DAO using JPA bq. This page has been fully written by "Tony Clonier":https://www.linkedin.com/in/tony-clonier-b44631138/ and validated by Thomas Broussard The purpose of this tutorial is to implement the DAO design pattern using Java and the JPA API. All of the following code can be found in my public repository: "https://bitbucket.org/BSpirit/dao-tutorial-core/src/master/":https://bitbucket.org/BSpirit/dao-tutorial-core/src/master/ h2(#project-set-up). Project Set up In this part, we are going to set up the Java project using Eclipse IDE and Apache Maven software. h3(#tools). Tools * *Eclipse IDE* Eclipse is a popular IDE used in Java programming. "https://www.eclipse.org/downloads/":https://www.eclipse.org/downloads/ * *Maven* Apache Maven is a software project management and comprehension tool. Based on the concept of a Project Object Model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information. Its main purpose for this tutorial will be to handle dependencies between librairies. "https://maven.apache.org/":https://maven.apache.org/ h3(#create-new-project). Create new project Open Eclipse IDE and create a new Maven Project: @File -> New -> Maven Project@ * You may check @Create a simple project@ option. This will create a basic, Maven-enabled Java project. If you require a more advanced setup, leave this setting unchecked, and you will be able to use more advanced Maven project setup features. Then, click @next@. * Fill up new project informations, then click @finish@. !img/create-project.png(Project creation)! * The structure of a Maven Based project is as follow: !img/project-tree.png(Project tree)! h3(#adding-dependencies). Adding dependencies The dependencies of the project can be found in the @pom.xml@ file located at the root of the project. To begin with, we are going to add @Lombok@ and @Hibernate@ dependencies: * include lombok, add it to your @@ block like so: bc(xml). org.projectlombok lombok 1.18.2 provided * To include Hibernate, add it to your @@ block like so: bc(xml). org.hibernate hibernate-core 5.3.7.Final * To force the project to compile using Java 8, you should also add this: bc(xml). 1.8 1.8 * Your final @pom.xml@ file should look like this: bc(xml). 4.0.0 fr.epita.tutorial dao-tutorial-core 0.0.1-SNAPSHOT dao-tutorial-core 1.8 1.8 org.projectlombok lombok 1.18.2 provided org.hibernate hibernate-core 5.3.7.Final * Once the dependency is added, you must use @maven install@ command to set the project. In eclipse, you can right click on the @pom.xml@ file located at the root of the project and go to @Run As -> Maven install@. *NOTE*: When adding new dependencies, you may right click on the root of the project and go to @Maven -> Update Project@ to get the new dependencies. h3(#lombok-installation). Lombok installation * Once the jar was downloaded in your local repository, go to the jar location from command prompt. If you are not sure where to find it, the path is specified in Eclipse: !img/lombok-path.png(Lombok Path)! * Then use @java -jar lombok-1.18.2.jar@ to pursue Lombok installation. You may have to restart Eclipse. h2(#entities). Entities Entities are POJO (Plain Old Java Object) class whose main purpose is to be mapped to relationnal database tables. In this part of the tutorial, we will use JPA API (thanks to Hibernate) and Lombok to easily create Entity classes. h3(#tools-1). Tools * *Project Lombok* Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. "https://projectlombok.org/":https://projectlombok.org/ * *Hibernate* Java Persistence API purpose is to persistently store the vast amounts of data into a database. Using JPA, the burden of interacting with the database reduces significantly. It forms a bridge between object models (Java program) and relational models (database program). In this tutorial, we will use Hibernate ORM, as it is also an implementation of the Java Persistence API (JPA) specification. "http://hibernate.org/orm/":http://hibernate.org/orm/ h3(#lets-write-some-code). Let's write some code ! To illustrate the DAO design pattern, we are first going to create two entities, an Author entity and a Book entity. Using @Javax.persistence@ and @Lombok@, the code is very simple. * First, let's create our Author class inside a @fr.epita.tutorial.datamodel@ package. Right click on @src/main/java@ and go to @new -> Class@. !img/author-class.png(Author class)! The code of the Author class is as simple as this ! bc(java). @Entity @Data public class Author { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String firstName; private String lastName; } The code of the Book class is as easy to write: bc(java). @Entity @Data public class Book { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String title; @ManyToOne private Author author; } In these examples, several annotations are used to avoid writing redundant code: * @Data@ is a Lombok annotation and allows auto generation of setters/getters and other utilities function such as @toString@, @hashCode@ or @equals@ * @Entity@ is used to create the binding between our class and the DB table * @Id@ is used to specify which attribut is used as an ID inside the DB table * @ManyToOne@ is used to specify the relation between our Book class and our Author class (this will also impact the DB tables) h2(#dao). 3. DAO The Data Access Object (DAO) pattern is a structural pattern that allows isolation of the application/business layer from the persistence layer (usually a relational database, but it could be any other persistence mechanism) using an abstract API. The functionality of this API is to hide from the application all the complexities involved in performing CRUD operations in the underlying storage mechanism. This permits both layers to evolve separately without knowing anything about each other. In this part of the tutorial, we will create simple DAOs allowing an easy access to our DB entities. We will also use dependency injection to help us instanciate the @DriverManagerDataSource@ and the @SessionFactory@ objects, which are both necessary to handle the DB connection. h3(#tools-2). Tools * *Log4j* Apache Log4j is a Java-based logging utility. It can be used to help debugging the application if necessary. It can be added as a maven dependency by updating your @pom.xml@ file: bc. org.apache.logging.log4j log4j-api 2.11.1 org.apache.logging.log4j log4j-core 2.11.1 To use Log4j, you have to create a configuration file. Create a file called @log4j2.xml@ in @src/test/resources/@ and add this content: bc(xml). "https://logging.apache.org/log4j/2.x/":https://logging.apache.org/log4j/2.x/ * *Spring Framework* The Spring Framework is an application framework. In this part of the tutorial, it will be used for dependency injection using @Inject@ annotation from @javax.inject@. These can be added as maven dependencies by updating your @pom.xml@ file: bc.. javax.inject javax.inject 1 org.springframework spring-test 5.0.9.RELEASE test org.springframework spring-core 5.0.9.RELEASE org.springframework spring-context 5.0.9.RELEASE org.springframework spring-orm 5.0.9.RELEASE p. "https://spring.io/":https://spring.io/ h3(#lets-write-some-code-1). Let's write some code ! * First, let's create, inside a @fr.epita.tutorial.datamodel@ package, a generic DAO able to perform basic CRUD operations. Right click on @src/main/java@ and go to @new -> Class@. The DAO code is as follow: bc.. public abstract class DAO { protected static final Logger LOGGER = LogManager.getLogger(DAO.class); protected Class modelClass; @Inject @Named("sessionFactory") private SessionFactory sf; public void setmodelClass(Class modelClass) { this.modelClass = modelClass; } protected final Session getSession() { Session session = null; try { session = this.sf.getCurrentSession(); } catch (Exception e) { LOGGER.error(e); } if (session == null) session = sf.openSession(); return session; } protected final Transaction getTransaction(Session session) { Transaction tx = session.getTransaction(); if (!TransactionStatus.ACTIVE.equals(tx.getStatus())) tx = session.beginTransaction(); return tx; } public final Long create(T obj) { Session session = this.getSession(); Transaction tx = this.getTransaction(session); Long id = (Long) session.save(obj); tx.commit(); return id; } public final void delete(T obj) { Session session = this.getSession(); Transaction tx = this.getTransaction(session); session.delete(obj); tx.commit(); } public final void update(T obj) { Session session = this.getSession(); Transaction tx = this.getTransaction(session); session.update(obj); tx.commit(); } public T getById(Long id) { return getSession().get(modelClass, id); } @SuppressWarnings("unchecked") public List getAll() { return getSession().createQuery("from " + modelClass.getName()).list(); } public abstract List search(T criteria); } p. In the previous exemple: * The @SessionFactory@ object is used to create Sessions and Transactions with the DB. We will see later how to configure it. * @getSession@ and @getTransaction@ methods are used to avoid Sessions or Transactions duplication. * @create@, @update@, @update@, @getById@ and @getAll@ methods perform the basic CRUD actions. As you can, we don't even need to know what the data looks like thanks to genericity. Because this DAO class is abstract, it cannot be instantiated. Specific DAOs must be created for each entity and can be used to specify some behaviors, which might depends on the entity itself: * *AuthorDAO*: bc.. @Repository public class AuthorDAO extends DAO { public AuthorDAO() { this.setmodelClass(Author.class); } @Override public List search(Author criteria) { String searchQuery = "from Author as a " + "where a.firstName like :firstName or " + "a.lastName like :lastName"; Session session = this.getSession(); Query query = session.createQuery(searchQuery, Author.class); query.setParameter("firstName", "%" + criteria.getFirstName() + "%"); query.setParameter("lastName", "%" + criteria.getLastName() + "%"); return query.list(); } } p. * *BookDAO*: bc.. @Repository public class BookDAO extends DAO { public BookDAO() { this.setmodelClass(Book.class); } @Override public List search(Book criteria) { String searchQuery = "from Book where author = :author"; Query query = getSession().createQuery(searchQuery, Book.class); query.setParameter("question", criteria.getAuthor()); return query.list(); } } p. In the previous examples: * We use a create a query using HQL (Hibernate Query Language) and the @Query@ object. * We use @setParameter@ to make a binding using the criteria variable. * We return the result of the @Query@ as a list. * The @Repository annotation is needed by Spring Framework when creating a DAO class. For more details, check Spring Framework documentation. h3(#use-dependency-injection). Use dependency injection The @BookDAO@ and @AuthorDAO@ are both using dependency injection to instantiate the @SessionFactory@ object. As a result, these objects have to be managed by the Spring IoC container. These objects can be refered as "beans". These beans are created with the configuration metadata supplied, for example using an XML file. Let's create this XML file: * Create a file called @applicationContext.xml@ in @src/test/resources@ and add this content inside: bc.. org.hibernate.dialect.H2Dialect true update false fr.epita.tutorial.datamodel p. Let's explain this configuration a little bit more: * @@ is used to scan the given package in order to make the @BookDAO@ and @AuthorDAO@ classes recognizable as Beans and injected. * @@ is used to instantiate the @SessionFactory@ object. * This Bean need both @dataSource@and @hibernateProperties@ to be instantiated. h2(#tests-with-junit). 4. Tests with JUnit In this session, we are going to use JUnit to test some of the CRUD operations and demonstrate how dependency injection works. h3(#tools-3). Tools * *H2 Database* H2 is a relational database management system written in Java. It can be embedded in Java applications or run in the client-server mode. It will be used to store our data. It can be added as a maven dependency by updating your @pom.xml@ file: bc(xml). com.h2database h2 1.4.197 test "http://www.h2database.com/html/main.html":http://www.h2database.com/html/main.html * *JUnit* JUnit framework will be used for unit tests. It can be added as a maven dependency by updating your @pom.xml@ file: "https://junit.org/junit4/":https://junit.org/junit4/ h2(#lets-write-some-code-2). Let's write some code ! * Let's create some tests inside a @fr.epita.tutorial.services.test@ package. Right click on @src/test/java@ and go to @new -> Class@. The code is as follow: bc.. @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/applicationContext.xml") public class TestJPA { private static final Logger LOGGER = LogManager.getLogger(TestJPA.class); @Inject AuthorDAO authorDAO; @Inject BookDAO bookDAO; @Before public void addAuthor() { Author author = new Author(); author.setFirstName("Philip"); author.setLastName("K. Dick"); authorDAO.create(author); } @Test public void testAuthorDAOCreateGetById() { // Given Author author = new Author(); author.setFirstName("Stephen"); author.setLastName("King"); // When Long id = authorDAO.create(author); // then author = authorDAO.getById(id); LOGGER.info(author); Assert.assertEquals("Stephen", author.getFirstName()); Assert.assertEquals("King", author.getLastName()); } @Test public void testAuthorDAOSearchUpdate() { // Given Author author = new Author(); author.setFirstName("Philip"); author = authorDAO.search(author).get(0); // When author.setFirstName("Howard"); author.setLastName("Lovecraft"); authorDAO.update(author); // Then author = authorDAO.search(author).get(0); LOGGER.info(author); Assert.assertEquals("Howard", author.getFirstName()); Assert.assertEquals("Lovecraft", author.getLastName()); } @Test public void testAuthorDAODeleteGetAll() { // Given List authors = authorDAO.getAll(); // When for (Author author : authors) authorDAO.delete(author); // Then authors = authorDAO.getAll(); LOGGER.info(authors); Assert.assertEquals(0, authors.size()); } } p. In the previous example: * Because we are using dependency injection, we have to specify the application context using: bc(java). @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/applicationContext.xml") * As you can see, @Inject@ annotation is used for dependency injection and allow the instantiation of @BookDAO@, @AuthorDAO@ and their corresponding @SessionFactory@ object. p(breadcrumb). "Tutorials":../../../tutorials > Advanced > "jpa-dao":../jpa-dao > jpa-dao.textile