I am exploring JPA and Ebean within Play Framework 2.0.1 to research what is generated in a local relational database. In my tests, I’m using MariaDB, a fork of MySQL.
So far, I’ve been able to successfully generate tables in the database using the JPA annotations within my models. I wanted to make sure that I learned how to set up an entity with a OneToOne relationship to another entitiy, as well as a OneToMany to another.
OneToOne: Employee has Address
OneToMany: Employee has Pet(s)
I set up a small application that allows me to quickly populate the tables, display some of the info, and delete rows from the tables. Here is the code below for those who want to see my setup:
Routes
GET / controllers.Application.index()
GET /add controllers.Application.addEmployee()
GET /delete/:id controllers.Application.deleteEmployee(id: Long)
GET /employee/:id controllers.Application.showEmployee(id: Long)
Controller
Application.java
public class Application extends Controller {
public static Result index() {
return ok(index.render("entry point"));
}
public static Result addEmployee() {
// Create an employee.
Employee employee = new Employee();
employee.firstName = "John";
employee.lastName = "Doe";
employee.salary = new BigDecimal(123456);
// Create an address for the employee.
Address address = new Address();
address.city = "West Chester";
address.country = "United States of America";
address.postalCode = "19380";
address.province = null;
address.street = "45 Jingleheimer Drive";
// Create pets for the employee.
Pet pet = new Pet();
pet.petString = "dog";
Pet pet2 = new Pet();
pet2.petString = "cat";
employee.address = address;
employee.pets.add(pet);
employee.pets.add(pet2);
employee.save();
return ok(index.render("added an employee"));
}
public static Result showEmployee(Long id) {
Employee e = Employee.get(id);
//String s = e.address.street;
return ok(employee.render(e));
}
public static Result deleteEmployee(Long id) {
Employee.delete(id);
return ok(index.render("deleted employee " + id));
}
}
Models
Employee.java
@Entity
public class Employee extends Model {
@Id
public Long employee_id;
public String firstName;
public String lastName;
public BigDecimal salary;
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="owner_id")
public Address address;
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="owner_id", referencedColumnName="employee_id")
public List<Pet> pets;
public static Employee get(Long id) {
return find.byId(id);
}
public static void delete(Long id) {
find.byId(id).delete();
}
public static Finder<Long, Employee> find = new Finder<Long,Employee>(Long.class, Employee.class);
}
Address.java
@Entity
public class Address extends Model {
@Id
public Long address_id;
public String street;
public String city;
public String province;
public String country;
public String postalCode;
}
Pet.java
@Entity
public class Pet extends Model {
@Id
public Long pet_id;
public String petString;
}
Views
employee.scala.html
@(employee: models.Employee)
@import helper._
@main("employee view") {
<p>@employee.firstName</p>
<p>@employee.lastName</p>
<p>@employee.salary</p>
<p>@employee.address.country</p>
<p>@employee.pets(1).petString</p>
@for(pet <- employee.pets) {
<p>@pet.petString</p>
}
}
If I render the view above using a URL like localhost:9000/employee/1 I get the following displayed in the browser:
John
Doe
123456
cat
dog
cat
Notice that the address’s country is not displayed. I’ve also tried accessing it in the controller and printing it to the command window, which prints NULL.
However, I found that if I added getters to the Address class, I can retrieve it in the scala template:
Adding
public String getCity() {
return city;
}
...
to Address.java results in
John
Doe
123456
United States of America
cat
dog
cat
So I am able to set an address and assign it to an employee (it shows up in my DB). Also, deleting an employee correctly cascades and deletes the Address in the database. Likewise, this works for the Pet class which is OneToMany. So why do I need to add getters to access my Address properties, but not for Pet? Is my one-to-one relationship not set up correctly in the Java annotations?
EBean does not support lazy loading with direct property access.
Within Java-Controller-Code, the statement
employee.address.countryis enhanced to something likeemployee.getAddress().getCountry(). This, alas, is not done in Scala code, specifically not in the templates.employee.pets(1).petString, on the other hand, is translated toemployee.pets.get(1).petString, which is enough for EBean to lazy load, because an EBean LazyList is involved.Don’t stick back to getters/setters please! The Play! people argue you should know in the controller what data you need and fetch it at query time: