JPA Transparent Indirection and Container Policies - jpa

Suppose I have the following simple Customer/Order implementation:
A record of customers defined by a Customer class.
Each customer can have multiple orders defined by an Order class.
Drawing on the explanation of Transparent Indirection from here and Container Policies from here my understanding of these concepts EclipseLink is as follows:
Transparent Indirection allows me to say
Customer customer = Customer.getCustomerById(1);
Set<Order> orders = customer.getOrders();
Two points to note are:
Indirection allows lazy loading of attributes so a customer's orders are only fetched from the DB on line 2, not line 1.
I can treat the orders of a customer as a Set (or Collection or List or Map) of objects of type Order.
The Container Policy tells to EclipseLink which actual class should be used for the Set and it should therefore implement Set in the example above.
That is my understanding of Transparent Indirection and Container Policies in EclipseLink.
I am seeing the following error when I try to access the database:
Exception [EclipseLink-148] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: The container policy [CollectionContainerPolicy(class org.eclipse.persistence.indirection.IndirectSet)] is not compatible with transparent indirection.
Mapping: org.eclipse.persistence.mappings.OneToManyMapping[orders]
Descriptor: RelationalDescriptor(my.model.Customer --> [DatabaseTable(Customer)])
I'm sure I have an error in my code somewhere which I am trying to debug but I didn't specify the CollectionContainerPolicy mentioned in the error so I assume org.eclipse.persistence.indirection.IndirectSet is the default. But if I'm using the default policy then I'm not sure what the cause of this error may be or which policy I should be using.
For now, I'd just like to know if my understanding of Transparent Indirection and Container Policies as I mentioned above is correct.
If it is correct I'm probably missing something relatively small in my code (an invocation or configuration option etc.) but if I'm not understanding the concepts then clearly I need to do more research first.
Customer model
package my.model;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* The persistent class for the customer database table.
*
*/
#Entity
#Table(name=Customer.TBL_NAME)
#NamedQueries({
#NamedQuery(name=Customer.QRY_BY_NAME,query="Select object(a) from Customer a where " +
"a.name=:" + Customer.PRM_NAME),
#NamedQuery(name=Customer.QRY_ALL, query="select object(a) from Customer a")
})
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
// Table specific onstants
public static final String TBL_NAME = "Customer";
public static final String QRY_BY_NAME = TBL_NAME + ".byName";
public static final String QRY_ALL = TBL_NAME + ".all";
public static final String PRM_NAME = "name";
private int id;
private String name;
private Set<Order> orders;
public Customer() {
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
//bi-directional many-to-one association to Order
#OneToMany(mappedBy="customer")
public Set<Order> getOrders() {
return this.orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
Order model
package my.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* The persistent class for the order database table.
*
*/
#Entity
#Table(name=Order.TBL_NAME)
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
// Table constants
public static final String TBL_NAME = "Order";
private int id;
private Customer customer;
public Order() {
}
#Id
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
//bi-directional many-to-one association to Customer
#ManyToOne
public Customer getCustomer() {
return this.customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}

Your understanding is correct, but shouldn't be needed as this isn't something you need to configure when using JPA. EclipseLink will determine the collection policy and implementation to use based on the type of the property and the lazy/eager setting, and it seems to be doing so correctly. The exception is thrown in error, probably due to classloader issues so that the classloader used for init isn't the one used to validate against, but I don't know how that could happen. You will need to look at the environment this is running in as the exception itself is just a symptom

Related

ebean query 2 models and render to html

I have setup 2 models in ebean, both work fine when queried separately. Now i need to get into my Show.scala.html fields from both my product & heading models, the product fields work fine but I also need the DateOrder from heading, but I am getting a compilation error of value DateOrder is not a member of object models.Heading, please help
My Models
package models;
import io.ebean.Finder;
import io.ebean.Model;
import play.data.validation.Constraints;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
#Entity
#Table(name="Heading")
public class Heading extends Model {
#Id
#Column(name="JobKeyID")
public Integer JobKeyID;
#Constraints.MaxLength(50)
#Constraints.Required
#Column(name="Name")
public String Name;
#Column (name="JobNumber", columnDefinition = "NVARCHAR")
public String JobNumber;
#Constraints.Required
#Column(name="SellingPriceIncTax")
public Integer SellingPriceIncTax;
#Constraints.Required
#Column(name="DateOrder")
public Date DateOrder;
#Column(name="CustomerID")
public Integer CustomerID;
#OneToMany(mappedBy = "heading", fetch = FetchType.EAGER)
public List<Product> product;
public static Finder<Integer, Heading> find = new Finder<>(Heading.class);
}
package models;
import io.ebean.Finder;
import io.ebean.Model;
import io.ebeaninternal.server.type.ScalarTypeJsonList;
import play.data.validation.Constraints;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
#Entity
#Table(name="Product")
public class Product extends Model {
#Id
#Column(name = "ItemKeyID")
public Integer ItemKeyID;
#Constraints.MaxLength(50)
#Constraints.Required
#Column(name = "ProductName")
public String ProductName;
#Column(name = "ItemNumber")
public Integer ItemNumber;
#Constraints.Required
#Column(name = "DesignName")
public String DesignName;
#Column(name = "JobKeyID")
public Integer JobKeyID;
#ManyToOne
#JoinColumn(name="JobKeyID")
public Heading heading;
#OneToMany(mappedBy = "product")
public List<Information> information;
public static Finder<Integer, Product> find = new Finder<>(Product.class);
}
My Controller
public Result show(Integer id){
List<Product> products = Ebean.find(Product.class)
.fetch("heading")
.where().eq("JobKeyID",id)
.setFirstRow(0)
.setMaxRows(10)
.findList();
return ok(show.render(products)) ;
}
and my scala.html
#(products : List[Product])
#Layout("All Books") {
<h1> All Books</h1>
#for(product <- products) {
<a class="btn btn-link" href="#routes.BooksController.specinfo(product.JobKeyID, product.ItemKeyID)">#product.ProductName</a>
<p> Design Name : #product.DesignName</p>
<p> Order Date : #Heading.DateOrder</p>
<img src ="#routes.ImagesController.getImage("419326-1.svg")"/>
}
}
You're trying to get field DateOrder via class, but it's not a static member. I believe it's the cause. If I understood your intention correctly, you should replace #Heading.DateOrder with #product.heading.DateOrder.

FindBy method returnign all records in PagingAndSortingRepository

Im using a findBy method in a interface that extends PagingAndSortingRepository. This method is:
public List<MyType> findByClassification(String classification);
When I invoke this method (myObject.findByClassification("A")), it returns all values and not only records filtered by classification "A".
The class model:
package mypackage;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "mytype")
public class MyType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
private String name;
private String classification;
//getters and setters
}
Other find methods work fine:
findByClassificationAndName_StartingWith
findByName
These methods return only filtered records and not all records in table.
Any idea?
Thanks!

#ManyToOne seems not working as i expect it to be

I'm working with many-to-one relationship between my two tables(Employee and Department) in which any Employee can work in more than one department. I used the #ManyToOne annotation on the Department object field which I created in Employee entity class. Now when I persist the Employee entity with a particular department, it works fine but when I try to persist another Employee entity with the same department, it creates a new Department entity with the same name and persists it with different id. What I expect it to do is that when I persist an Employee entity with already persisted department, it should just update the foriegn key of the Employee entity to point the id of that department. Sorry if I didnt got the many-to-one concept totally.
EMPLOYEE entity
package com.test.domain;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.TableGenerator;
#Entity
public class Employee {
#TableGenerator(name="Empl_Gen", table="ID_GEN",pkColumnName="GEN_NAME",valueColumnName="GEN_VALUE", initialValue=0, allocationSize=1)
#Id#GeneratedValue(generator="Empl_Gen",strategy=GenerationType.TABLE)
private Long id;
private String Name;
private String Country;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="DEPT_ID")
private Department department;
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getCountry() {
return Country;
}
public void setCountry(String country) {
Country = country;
}
}
DEPARTMENT entity
package com.test.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.TableGenerator;
#Entity
public class Department {
#TableGenerator(name="DEP_GEN",table="ID_GEN",pkColumnName="GEN_NAME",valueColumnName="GEN_VALUE", pkColumnValue="DEP_GEN",initialValue=0,allocationSize=1)
#Id#GeneratedValue(strategy=GenerationType.TABLE,generator="DEP_GEN")
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.test.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.test.domain.Employee;
import com.test.service.EmployeeService;
/**
* Handles requests for the application home page.
*/
#Controller
#RequestMapping("/addEmployee")
public class HomeController {
#Autowired
EmployeeService service;
#RequestMapping(method=RequestMethod.GET)
public String getform(Model model)
{
model.addAttribute("employee",new Employee());
return "home";
}
#RequestMapping(method=RequestMethod.POST)
public String setform(Employee employee)
{
service.save(employee);
return"success";
}
}
package com.test.dao;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.test.domain.Employee;
#Repository
#Transactional
public class Employeedaoimpl implements Employeedao
{
#PersistenceContext
EntityManager manager;
#Override
public void save(Employee employee) {
manager.persist(employee);
}
}
Try this:
#TableGenerator(name="DEP_GEN",table="ID_GEN",pkColumnName="GEN_NAME",valueColumnName="GEN_VALUE", pkColumnValue="DEP_GEN",initialValue=0,allocationSize=1)
#Id#GeneratedValue(strategy=GenerationType.TABLE,generator="DEP_GEN")
#Column(name = "DEPT_ID")
private Long id;

JPA QueryException: The list of fields to insert into the table is empty

I actually have a problem with JPA. He is complaining about one
of my Entities:
javax.persistence.RollbackException: Exception [EclipseLink-6023] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: The list of fields to insert into the table [DatabaseTable(TBL_SPJ_CAST_MOVIE)] is empty. You must define at least one mapping for this table.
After some researching i found out that this Exception appears when
there are no columns defined in the Entity classe because ofcourse
you cant insert Data into an empty Table.
However i have actually two columns defined in my Entity. One for the id
and one for a foreign key to a m:n table:
import java.io.Serializable;
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.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
#Entity
#Table(name="TBL_SPJ_CAST_MOVIE")
public class CastMovie implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="CAST_MOVIE_ID", unique=true)
private int castID = 0;
#ManyToMany(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinTable(name="TBL_SPJ_CAST_ROLE", joinColumns={#JoinColumn(name="CAST_MOVIE_ID")}, inverseJoinColumns={#JoinColumn(name="ROLE_ID")})
private List<Role> roles = null;
public CastMovie() {}
public int getCastID() {
return castID;
}
public void setCastID(int castID) {
this.castID = castID;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
So whats the point of this Error? All Data coming from a xml file so i cant
put in more columns (if thats the problem).
EDIT:
Here is the structure of the four tables involved in this problem:
One Movie has one Cast -> Relationship One to One
Many Casts have many Rols -> Relationship Many to Many
I had the same problem.
Set the GenerationType of id field to GenerationType.TABLE and it will do the trick.
Also read this

jpa named query not found

I am migrating an ejb 2 application to ejb 3.1. Both technologies will have to coexist for a while. One of my entity beans looks like this:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
#Entity
#Table(name = "migracao_jsf")
#NamedQueries({
#NamedQuery(name = "migracao_query", query = "select p from MigracaoJsf p")
})
public class MigracaoJsf implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "name")
#Id
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
On the deployment logs in jboss 6.1 I can see that the entity was deployed.
[AnnotationBinder] Binding entity from annotated class: myclass.MigracaoJsf
But I can't find the log for the query parsing. And when I try to call it I get a query not found exception. Why is the entity deployed correctly and the query not parsed?
Thanks
Kelly
You should be able to call the query using the following code:
EntityManager em;
// em is created sonehow
TypedQuery<MigracaoJsf> q = em.createNamedQuery("migracao_query", MigracaoJsf.class);
Also it is common practice to prefix the named queries with the class name since query names must be unique. Thus a query to get an entity by its name should be named Entity.GetByName.