Friday, July 10, 2015

Bidirectional relationship with Jackson in REST web services in WildFly

This is an example for a Bidirectional relationship between Java entities in a REST web service with the Jackson API.

1- Assume we have a bidirectional relationship between two entities Parent and Child.
2- Using MySQL workbench to generate the SQL schema file for this two tables.
DROP SCHEMA IF EXISTS `bidirectional_schema` ;
CREATE SCHEMA IF NOT EXISTS `bidirectional_schema` DEFAULT CHARACTER SET utf8 ;
USE `bidirectional_schema` ;

-- -----------------------------------------------------
-- Table `bidirectional_schema`.`PARENT`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `bidirectional_schema`.`PARENT` ;

CREATE  TABLE IF NOT EXISTS `bidirectional_schema`.`PARENT` (
  `PARENT_ID` INT NOT NULL ,
  `PARENT_CONTENT` VARCHAR(45) NULL ,
  PRIMARY KEY (`PARENT_ID`) )
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `bidirectional_schema`.`CHILD`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `bidirectional_schema`.`CHILD` ;

CREATE  TABLE IF NOT EXISTS `bidirectional_schema`.`CHILD` (
  `CHILD_ID` INT NOT NULL ,
  `CHILD_CONTENT` VARCHAR(45) NULL ,
  `PARENT_PARENT_ID` INT NOT NULL ,
  PRIMARY KEY (`CHILD_ID`) ,
  INDEX `fk_CHILD_PARENT_idx` (`PARENT_PARENT_ID` ASC) ,
  CONSTRAINT `fk_CHILD_PARENT`
    FOREIGN KEY (`PARENT_PARENT_ID` )
    REFERENCES `bidirectional_schema`.`PARENT` (`PARENT_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;
3- Then we will insert some data to those two tables through this insert statements
INSERT INTO `bidirectional_schema`.`parent` (`PARENT_ID`, `PARENT_CONTENT`) VALUES ('1', 'First Parent');
INSERT INTO `bidirectional_schema`.`child` (`CHILD_ID`, `CHILD_CONTENT`, `PARENT_PARENT_ID`) VALUES ('1', 'First Child', '1');
INSERT INTO `bidirectional_schema`.`child` (`CHILD_ID`, `CHILD_CONTENT`, `PARENT_PARENT_ID`) VALUES ('2', 'Second Child', '1');
4- The Maven dependencies for the Jackson API that are required for this example.
<dependency>
 <groupid>com.fasterxml.jackson.core</groupid>
 <artifactid>jackson-annotations</artifactid>
 <version>2.5.2</version>
</dependency>
<dependency>
 <groupid>com.fasterxml.jackson.core</groupid>
 <artifactid>jackson-core</artifactid>
 <version>2.5.2</version>
</dependency>
<dependency>
 <groupid>com.fasterxml.jackson.core</groupid>
 <artifactid>jackson-databind</artifactid>
 <version>2.5.2</version>
</dependency>
Also, you can pick up your required Java EE dependencies from this link.
https://wikis.oracle.com/display/GlassFish/Java+EE+7+Maven+Coordinates
5- Using Eclipse to generate Entities from the above tables.
File -> New -> Other, then from the wizard choose JPA Entities from Tables. A wizard will show up to create a connection to the schema that's already created. Then select the two tables child and parent. Finally make sure that the "List generated classes in persistence.xml".
You can proceed and follow the default settings but I chose the mapping settings as bellow:
6- There is nothing fancy about the persistence.xml. But there are two points to consider. First, I didn't specify the persistence provider as I tend to use WildFly so the application will use the default persistence provider which is Hibernate. Second, I created a datasource on WildFly so that it can be used to connect to our schema. Thirds, I used EclipseLink that's why I'm using this JPA provider org.eclipse.persistence.jpa.PersistenceProvider. Check this post to point WildFly to EclipseLink.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="BidirectionalPU">
     <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
     <jta-data-source>java:jboss/datasources/Bidirectional_DataSource</jta-data-source>
     <class>com.ithinkisink.entity.Child</class>
     <class>com.ithinkisink.entity.Parent</class>
 </persistence-unit>
</persistence>

7- The first class that we will implement is the ApplicationConfiguration class that will point to the parent path of our coming REST services.
package com.ithinkisink;

import javax.inject.Singleton;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

/**
 * 
 * @author Belal
 *
 */
@Singleton
@ApplicationPath("rest")
public class ApplicationConfiguration extends Application {
}
8- Then we will create MyService class that will have two REST web services. The first will retrieve a parent with a children fetched. The second one is an opposite to this and will retrieve a child with it's parent fetched.
package com.ithinkisink.service;

package com.ithinkisink.service;

import javax.ejb.EJB;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.ithinkisink.entity.Child;
import com.ithinkisink.entity.Parent;

/**
 * 
 * @author Belal
 *
 */
@Path("MyService")
public class MyService {

 @EJB
 MyServiceDAO myServiceDAO;
 
 @GET
 @Path("/retrieveParentWithChildren")
 @Produces(MediaType.APPLICATION_JSON)
 public Parent retrieveParentWithChildren() {
  return myServiceDAO.retrieveParentWithChildren();
 }
 
 @GET
 @Path("/retrieveChildWithParent")
 @Produces(MediaType.APPLICATION_JSON)
 public Child retrieveChildWithParent() {
  return myServiceDAO.retrieveChildWithParent();
 }
}

9- Then we will create ServiceDAO and notice that the EntityManager is injected with a connection to the persistence unit that we already defined in the persistence.xml. I passed the name of the persistence unit to make this point clear. But you can just use the annotation @PersistenceContext without specifying the unitName attribute then it will use our persistence unit because there is no other persistence unit defined in our application.
package com.ithinkisink.service;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.ithinkisink.entity.Child;
import com.ithinkisink.entity.Parent;

@Stateless
public class MyServiceDAO {
 
 @PersistenceContext(unitName="BidirectionalPU")
 protected EntityManager entityManager;
 
 public Parent retrieveParentWithChildren() {
  int parentId = 1;
  Query q = entityManager.createQuery("SELECT p FROM " + "Parent " + "p JOIN FETCH p.childs WHERE p.parentId = :parentId ");
  q.setParameter("parentId", parentId);
  return (q.getResultList().size() > 0) ? (Parent) q.getResultList().get(0) : null;
 }
 
 public Child retrieveChildWithParent() {
  int childId = 1;
  Query q = entityManager.createQuery("SELECT c FROM " + "Child " + "c JOIN FETCH c.parent WHERE c.childId = :childId ");
  q.setParameter("childId", childId);
  return (q.getResultList().size() > 0) ? (Child) q.getResultList().get(0) : null;
 }
}

10- Now that our application is ready for testing will add it to the WildFly server. Then starting the server should be successful.
11- For the sake of simplicity, I used Postman to test the running REST web services. So bellow are screenshots from the result on Postman.


You can find the complete example on my Github from this link:
https://github.com/belalgalal/Blogging/tree/master/BidirectionalRESTJson

No comments:

Post a Comment