Relational data modeling with JPA

JPA handles relations between data by using annotations.

Scenario:
Imagine a company with a few employees and some cars that are used by employees. Company can have many employees and each employee can have many cars.

Database schema:

database schema

Create new JavaSE project DerbyTest. Configure project build path to include derbyclient.jar, eclipselink.jar and javax.persistence_2.x.x.jar. First wee need to create three entity classes that will represent database entries and add relations between them. This is done by using annotations. JPA creates tables and relations according to annotations.

Company class:

package si.matjazcerkvenik.derby.relations;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Company {

@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int id;

private String description;


private List<Employee> employees = new ArrayList<Employee>();

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

@OneToMany(mappedBy="company")
public List<Employee> getEmployees() {
return employees;
}

public void setEmployees(List<Employee> employees) {
this.employees = employees;
}

@Override
public String toString() {
return "Company["+id+"] " + description + ", "+employees.size()+" employees: " + getEmployeesToString();
}

private String getEmployeesToString() {
String s = "";
for (Employee e : employees) {
s += e.toString();
}
return s;
}

}

Employee class:

package si.matjazcerkvenik.derby.relations;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

@Entity
public class Employee {

@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private String id;
private String firstname;
private String lastname;

private Company company;

private String dummyField = "";

private List<Car> carList = new ArrayList<Car>();

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getFirstname() {
return firstname;
}

public void setFirstname(String firstname) {
this.firstname = firstname;
}

public String getLastname() {
return lastname;
}

public void setLastname(String lastname) {
this.lastname = lastname;
}

@ManyToOne
public Company getCompany() {
return company;
}

public void setCompany(Company company) {
this.company = company;
}

@Transient
public String getDummyField() {
return dummyField;
}

public void setDummyField(String dummyField) {
this.dummyField = dummyField;
}

@OneToMany
public List<Car> getCarList() {
return carList;
}

public void setCarList(List<Car> carList) {
this.carList = carList;
}

@Override
public String toString() {
return "Employee["+id+"] Firstname: " + firstname + ", Lastname: " + lastname + ", cars: " + getCarsToString();
}

private String getCarsToString() {
String s = "";
for (Car c : carList) {
s += c.toString();
}
return s;
}


}

Car class:

package si.matjazcerkvenik.derby.relations;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Car {

@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private int id;
private String type;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}

@Override
public String toString() {
return "Car[" + id + "] " + type + " ";
}
}

Main class:

package si.matjazcerkvenik.derby.relations;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;


public class JpaRelationsDerbyTest {

private static final String PERSISTENCE_UNIT_NAME = "company";
private static EntityManagerFactory factory;

public static void main(String[] args) {

init();

fillDataFirstTime();
readEntries();

addNewEmployee();
readEntries();

String id = findEmployeeByName("Machu");
readEntries();

modifyEmployee(id);
readEntries();

boundEmployeeToCompany(id);
readEntries();

unboundEmployeeFromCompany(id);
readEntries();

removeEmployee(id);
readEntries();

}

public static void init() {
System.out.println("> init()");
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
}

@SuppressWarnings("unchecked")
public static void readEntries() {

System.out.println("> readEntries()");

EntityManager em = factory.createEntityManager();

Query q = em.createQuery("select c from Company c");

List<Company> companyList = q.getResultList();
for (Company company : companyList) {
System.out.println(company);
}
System.out.println("Size: " + companyList.size());

q = em.createQuery("select e from Employee e");

List<Employee> employeeList = q.getResultList();
for (Employee employee : employeeList) {
System.out.println(employee);
}
System.out.println("Size: " + employeeList.size());

q = em.createQuery("select a from Car a");

List<Car> carList = q.getResultList();
for (Car car : carList) {
System.out.println(car);
}
System.out.println("Size: " + carList.size());

em.close();
}

public static void fillDataFirstTime() {

EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Query q = em.createQuery("select e from Employee e");
boolean isEmpty = (q.getResultList().size() == 0);

if (isEmpty) {

System.out.println("> fillDataOnlyFirstTime()");

Company company = new Company();
company.setDescription("McDonalds");
em.persist(company);

Employee e1 = new Employee();
e1.setFirstname("John");
e1.setLastname("Burger");
em.persist(e1);
// company-employee relationship
company.getEmployees().add(e1);
em.persist(e1);
em.persist(company);

Employee e2 = new Employee();
e2.setFirstname("Lucy");
e2.setLastname("Cheese");
em.persist(e2);
// company-employee relationship
company.getEmployees().add(e2);
em.persist(e2);
em.persist(company);

Car c1 = new Car();
c1.setType("Volvo");
em.persist(c1);
// employee-car relationship
company.getEmployees().get(0).getCarList().add(c1);
em.persist(c1);
em.persist(company);
}

em.getTransaction().commit();
em.close();
}

public static void addNewEmployee() {
System.out.println("> addNewEmployee()");

EntityManager em = factory.createEntityManager();
em.getTransaction().begin();

Employee e = new Employee();
e.setFirstname("Machu");
e.setLastname("Pichu");

em.persist(e);

em.getTransaction().commit();
em.close();
}


public static String findEmployeeByName(String firstname) {
System.out.println("> findEmployeeByName()");

EntityManager em = factory.createEntityManager();

Query q = em.createQuery("select e from Employee e where e.firstname=?1");
q.setParameter("1", firstname);
Employee e = (Employee) q.getSingleResult();
System.out.println("> findEmployeeByName(): " + e);

em.close();

return e.getId();
}

public static void modifyEmployee(String id) {

System.out.println("> modifyEmployee(): id=" + id);

EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Employee e = em.find(Employee.class, id);

e.setFirstname("Brad");
e.setLastname("Pitt");

em.getTransaction().commit();
em.close();
}

public static void boundEmployeeToCompany(String id) {

System.out.println("> boundEmployeeToCompany(): id=" + id);

EntityManager em = factory.createEntityManager();
em.getTransaction().begin();

Query q = em.createQuery("select c from Company c where c.description=?1");
q.setParameter("1", "McDonalds");
Company company = (Company) q.getSingleResult();

Employee employee = em.find(Employee.class, id);

company.getEmployees().add(employee);
em.persist(employee);
em.persist(company);

em.getTransaction().commit();
em.close();

}


public static void unboundEmployeeFromCompany(String id) {

System.out.println("> unboundEmployeeFromCompany(): id=" + id);

EntityManager em = factory.createEntityManager();
em.getTransaction().begin();

Query q = em.createQuery("select c from Company c where c.description=?1");
q.setParameter("1", "McDonalds");
Company company = (Company) q.getSingleResult();

Employee employee = em.find(Employee.class, id);

int index = -1;
for (int i = 0; i < company.getEmployees().size(); i++) {
Employee e = company.getEmployees().get(i);
if (e.getId().equals(id)) {
index = i;
}
}
// release company-employee relationship
company.getEmployees().remove(index);

em.persist(employee);
em.persist(company);

em.getTransaction().commit();
em.close();

}


public static void removeEmployee(String id) {

System.out.println("> removeEmployee(): id=" + id);

EntityManager em = factory.createEntityManager();

Query q = em.createQuery("select e from Employee e where e.id=?1");
q.setParameter("1", id);
Employee e = (Employee) q.getSingleResult();

em.getTransaction().begin();
em.remove(e);
em.getTransaction().commit();
em.close();
}

}

The file persistence.xml can contain many persistence-unit definitions. PERSISTENCE_UNIT_NAME variable tells JPA which persistence-unit should be used for this application.

When running application for the first time, you need to uncomment the lines in persistence.xml to allow JPA to generate new empty tables in database.

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="company" transaction-type="RESOURCE_LOCAL" >
<class>si.matjazcerkvenik.derby.relations.Company</class>
<class>si.matjazcerkvenik.derby.relations.Employee</class>
<class>si.matjazcerkvenik.derby.relations.Car</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:derby://localhost:1527//Users/matjaz/Documents/EclipseWorkspace/DerbyTest/relationsTestDB;create=true" />

<property name="javax.persistence.jdbc.user" value="admin" />
<property name="javax.persistence.jdbc.password" value="admin" />

<!-- EclipseLink should create the database schema automatically -->
<!-- <property name="eclipselink.ddl-generation" value="create-tables" /> -->
<!-- <property name="eclipselink.ddl-generation.output-mode" -->
<!-- value="database" /> -->
</properties>

</persistence-unit>
</persistence>

Before running the application start Derby server:

$ java -jar $DERBY_HOME/lib/derbyrun.jar server start

Check the database to see the tables that were created. You should see that besides tables COMPANY, EMPLOYEE and CAR, two more tables were created: COMPANY_EMPLOYEE and EMPLOYEE_CAR. These two tables store relations between the Company and Employee entities.

See (here) for help on configuration of database management in Eclipse.

db picture