JPA Eclipselink JOIN FETCH LAZY relation returning null - jpa

I am always getting NULL from a JOIN FETCH clause in my JPA Query, even though I have everything configured as expected:
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#Entity
#Table(name = "TB_BANNER_IMAGE")
public class BannerImage extends BaseEntity<Integer> {
protected FileReference fileReference;
private String type;
private String labelTitle;
protected BannerImage() {}
#Id
#TableGenerator(name="genBannerImage", table="TB_ID_GENERATOR",
pkColumnName="ID_NAME", valueColumnName="ID_VAL",
pkColumnValue="TB_BANNER_IMAGE", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="genBannerImage")
#Column(name = "ID_BANNER_IMAGE", unique = true, nullable = false)
public Integer getId() {
return super.getId();
}
#Override
public void setId(Integer id) {
super.setId(id);
}
#Column(name="TYPE")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="ID_FILE_REFERENCE", nullable=false)
public FileReference getFileReference() {
return fileReference;
}
public void setFileReference(FileReference fileReference) {
this.fileReference = fileReference;
}
#Column(name="LABEL_TITLE")
public String getLabelTitle() {
return labelTitle;
}
public void setLabelTitle(String labelTitle) {
this.labelTitle = labelTitle;
}
}
for File Reference Class:
#Entity
#Table(name = "TB_FILE_REFERENCE")
public class FileReference extends BaseNamedEntity<String> {
private String type;
public FileReference() {}
#Id
#TableGenerator(name="genFileReference", table="TB_ID_GENERATOR",
pkColumnName="ID_NAME", valueColumnName="ID_VAL",
pkColumnValue="TB_FILE_REFERENCE", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="genFileReference")
#Column(name = "ID_FILE_REFERENCE", unique = true, nullable = false)
public String getId() {
return super.getId();
}
#Override
public void setId(String id) {
super.setId(id);
}
#Column(name = "TYPE")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Service class:
#Path("/banner")
public class BannerImageService extends BaseServiceFacade<BannerImage, Integer> {
#SuppressWarnings("unchecked")
#Override
public Crud<BannerImage, Integer> lookupService() throws ServiceLocatorException {
return ServiceLocator.getInstance()
.getLocalHome(ServicesConstants.BANNER_IMAGE_SERVICE);
}
#Override
protected String getDefaultGetQuery() {
return BannerImageDAO.GET_BY_ID_FETCH_FILE_REF;
}
#Override
protected String getDefaultQuery() {
return BannerImageDAO.GET_ALL_FETCH_FILE_REF;
}
}
get REST method of BaseServiceFacade:
#Override
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/{id}")
public T get(#PathParam("id") ID id) {
try {
if (!validateID(id)) {
logMessage("Invalid Entity ID: " + id);
return null;
}
String defaultGetQuery = getDefaultGetQuery();
if (defaultGetQuery != null) {
Map<String, Object> mapParams = new HashMap<String, Object>();
mapParams.put("id", id);
List<T> entityList = getService().search(defaultGetQuery, mapParams);
if (entityList != null && entityList.size() == 1) {
T ent = entityList.get(0);
return ent;
} else {
logMessage("Invalid search by Entity ID: " + id);
}
} else {
return getService().findById(clazz, id);
}
} catch (ServiceException e) {
serviceException(e);
} catch (Exception ex) {
logException(ex);
}
return null;
}
And finally the Service Bean EJB which reads from entityManager:
public class BaseServiceBean<T extends IEntity<ID>, ID extends Serializable> implements Crud<T,ID> {
// ... generic methods to be reused by subclasses
#Override
public List<T> search(String queryOrNamedQuery) throws ServiceException {
return search(queryOrNamedQuery, null, 0, 0);
}
#SuppressWarnings("unchecked")
public List<T> search(String namedQueryOrHql, Map<String, Object> parameters, int start, int chunkSize) {
try {
Query query = createQuery(namedQueryOrHql, getQueryType(namedQueryOrHql));
if (start > 0) {
query.setFirstResult(start);
}
if (chunkSize > 0) {
query.setMaxResults(chunkSize);
}
addParameters(query, parameters);
List<T> result = query.getResultList();
afterSearch(result);
return result;
} catch (NoResultException nre) {
nre.printStackTrace();
} catch (ClassCastException cce) {
cce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void afterSearch(List<T> result) {
}
// etc...
implementation specific class for BannerImageService:
#Stateless(mappedName="ejb/BannerImageService")
public class BannerImageServiceBean extends BaseServiceBean<BannerImage, Integer> implements BannerImageServiceBeanRemote, BannerImageServiceBeanLocal {
#Override
protected void afterSearch(List<BannerImage> result) {
if (result != null && result.size() == 1) {
BannerImage bannerImage = result.get(0);
bannerImage.getFileReference();
}
super.afterSearch(result);
}
// additional code ...
When I try to fetch my BannerImage class together with it's corresponding FileReference member I always get NULL even though in my DB there is an existing foreign key present:
JPQL:
"SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id";
Generated SQL:
SELECT t1.ID_BANNER_IMAGE, t1.LABEL_TEXT, t1.LABEL_TITLE, t1.TYPE,
t1.ID_FILE_REFERENCE, t0.ID_FILE_REFERENCE, t0.NAME,
t0.TYPE FROM TB_FILE_REFERENCE t0, TB_BANNER_IMAGE
t1 WHERE (t0.ID_FILE_REFERENCE = t1.ID_FILE_REFERENCE) AND t1.ID_BANNER_IMAGE = 1
in my DB the record shows a correct reference:
BANNER_IMAGE:
1;"";"main";"2bdbb063d0d0ee2939c89763945d9d9e";"banner1.png";"image/png"
If I execute :
select * from TB_FILE_REFERENCE where ID_FILE_REFERENCE = '2bdbb063d0d0ee2939c89763945d9d9e'
I can find the record in the DB, although my EclipseLink JPA Implementation always returns null:
EclipseLink Version 2.5.2-M1
This is how the Entity gets passed from Service Layer to the
Can someone help pointing why the JOIN FETCH is not properly working?

I faced a similar issue and looking closely I see that this issue was happening only to entities recently created/saved. Then I figured that it has something to do with eclipselink cache. I solved this problem by adding this line before making a join fetch JPQL query,
em.getEntityManagerFactory().getCache().evictAll();
em.createQuery("SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id").getResultList();
HTH!

Related

JPA Entity not stored OneToMany relationship

i trie to run the following code.
But the child is not created to the parent Entity 'Erfasser'.
If i comment out the line erfasser.getErfasst().add(neu) everything works fine.
#PostConstruct
public void init() {
Erfasser erfasser = new Erfasser();
erfasser.setEmail("benjamin.koubik#auditweb.de");
erfasser.setPasswort("counting88");
gesamtAnzahl.einfuegenErfasser(erfasser);
Erfasst neu = new Erfasst();
neu.setDatum(new Date());
neu.setJuristische(1);
neu.setNatuerliche(0);
gesamtAnzahl.einfuegen(neu);
erfasser.getErfasst().add(neu);
gesamtAnzahl.update(erfasser);
}
Only the Erfasser itself is stored correctly in the DB.
#Entity
public class Erfasser implements Serializable {
private static final long serialVersionUID = 1L;
public Erfasser() {
super();
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int erfasser_id;
#Column(length = 50)
#Email(message = "Inkorrekt EMail")
private String email;
#Column(length = 30)
private String passwort;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(referencedColumnName = "erfasser_id", name = "erfasst_id_referenz")
private List<Erfasst> erfasst;
public int getErfasser_id() {
return erfasser_id;
}
public void setErfasser_id(int erfasser_id) {
this.erfasser_id = erfasser_id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPasswort() {
return passwort;
}
public void setPasswort(String passwort) {
this.passwort = passwort;
}
public List<Erfasst> getErfasst() {
return erfasst;
}
public void setErfasst(List<Erfasst> erfasst) {
this.erfasst = erfasst;
}
}
And here my SessionBeans:
AnzahlErfasstGesamtLocal.java
#Local
public interface AnzahlErfasstGesamtLocal {
public abstract List<Integer> gesamt();
public abstract List<Erfasst> gesamtNatuerlich();
public abstract List<Erfasst> gesamtJuristisch();
public abstract void einfuegenErfasser(Erfasser e);
public abstract void einfuegen(Erfasst e);
public abstract void update(Erfasser e);
public abstract void loeschen(Erfasst e);
}
AnzahlErfasstGesamt.java
#Stateless
#LocalBean
public class AnzahlErfasstGesamt implements AnzahlErfasstGesamtLocal {
#PersistenceContext
private EntityManager em;
public AnzahlErfasstGesamt() {
}
#Override
public List<Integer> gesamt() {
return null;
}
#Override
public List<Erfasst> gesamtNatuerlich() {
try {
TypedQuery<Erfasst> q = em.createQuery(
"SELECT COUNT(e) FROM Erfasst e WHERE e.natuerliche = 1 AND e.juristische = 0; ", Erfasst.class);
List<Erfasst> liste = q.getResultList();
if (!liste.isEmpty()) {
return liste;
} else {
return null;
}
} catch (NoResultException e) {
return null;
}
}
#Override
public List<Erfasst> gesamtJuristisch() {
try {
TypedQuery<Erfasst> q = em.createQuery(
"SELECT COUNT(e) FROM Erfasst e WHERE e.juristische = 1 AND e.natuerliche = 0; ", Erfasst.class);
List<Erfasst> liste = q.getResultList();
if (!liste.isEmpty()) {
return liste;
} else {
return null;
}
} catch (NoResultException e) {
return null;
}
}
#Override
public void einfuegen(Erfasst e) {
em.persist(e);
}
#Override
public void update(Erfasser e) {
em.merge(e);
}
#Override
public void loeschen(Erfasst e) {
em.remove(em.merge(e));
}
#Override
public void einfuegenErfasser(Erfasser e) {
em.persist(e);
}
}
There is nothing wrong with JPA - something is wrong in external code (and certainly with your description of the problem). For example I don't see where the actual erfasst list is created - if nothing happens in einfuegenErfasser (whatever that means), then you will get a NullPointerException while trying to add an element to a null list. Is that what happens?
The problem is the combination of JPA entity setup and the code using it. The JPA entity Erfasser has CascadeType.ALL, therefore the gesamtAnzahl.update(erfasser); updates the child entities erfasst with it. At the same time you do not setup the erfasser reference on the neu instance. You need to do something alog the line neu.setErfasser(erfasser) before gesamtAnzahl.update(erfasser);.
On separated line of concern, using the native German naming drives my head crazy, even though I am more German then English speaker.

Unstanding JPA propagation of updates across related entities

UPDATED: I've taken Chris's answer into account but it didn't help - I'm still facing the issue. I've updated the code below to incorporate Chris's answer. Something to note is that when implementing Chris's suggestion, the relations were persisted but not reflected on the view.xhtml page. I had to replace the db object with the object returned form the call to GenericDao.update().
I've got the following relations:
One Customer to many PurchaseOrders (PO)
One PO to many Invoices.
I've read up on bi-directional relations and I know that if I have a bi-directional relation, I should update both sides of the relation when updating entities.
I perform the following steps:
Create a customer (Customer 1)
Create a purchase order (Order 1) linked to Customer 1
Create an invoice (Invoice 1) linked to Order 1.
What I observe is that all entities and relations are persisted but the list of Customer's Orders is not displayed.
view Customer:
view PurchaseOrder:
DB queries:
> select * from customer;
> +----+------------+
> | ID | NAME |
> +----+------------+
> | 1 | Customer 1 |
> +----+------------+
> 1 row in set (0.00 sec)
>
> mysql> select * from purchaseorder;
> +----+---------+-------------+
> | ID | NAME | customer_id |
> +----+---------+-------------+
> | 1 | Order 1 | 1 |
> +----+---------+-------------+
> 1 row in set (0.00 sec)
>
> mysql> select * from invoice;
> +----+-----------+------------------+
> | ID | NAME | purchaseorder_id |
> +----+-----------+------------------+
> | 1 | Invoice 1 | 1 |
> +----+-----------+------------------+
> 1 row in set (0.00 sec)
The DB reflects the relation between the configured entities so I know my changes are being persisted and, as far as I can tell, I've implemented the Customer-PO relation the same ways as the PO-Invoice relation. Since the list of Invoices for PO is updated correctly, I don't think I have a systemic issue so there must be something different between the implementation of Customers-PO and PO-Invoices relations but I can't spot it.
Why don't I see a list of PO's for my Customer even though there are clearly PO's configured for the customer?
Any help will be appreciated.
Classes(truncated for brevity):
Entities
Customer
private int id; //#Id and #GeneratedValue(IDENTITY) on getter
private String name;
#OneToMany(mappedBy="customer")
private Set<PurchaseOrder> purchaseOrders;
public Customer()
{
purchaseOrders = new HashSet<PurchaseOrder> ();
}
public Set<PurchaseOrder> getPurchaseOrders()
{
return this.purchaseOrders;
}
public void setPurchaseOrders(Set<PurchaseOrder> orders)
{
this.purchaseOrders = orders;
}
public void addPurchaseOrder(PurchaseOrder purchaseOrder)
{
this.purchaseOrders.add(purchaseOrder);
//this IF is important for avoiding an infinite loop
if (purchaseOrder.getCustomer() != this)
{
purchaseOrder.setCustomer(this);
}
}
public void removePurchaseOrder(PurchaseOrder purchaseOrder)
{
this.purchaseOrders.remove(purchaseOrder);
//this IF is important to avoid an infinite loop
if(purchaseOrder.getCustomer() != null)
{
purchaseOrder.removeFromCustomer(this);
}
}
PO
private int id; //#Id and identity column
private String name;
#ManyToOne
#JoinColumn(name="customer_id")
private Customer customer;
#OneToMany(mappedBy="purchaseOrder")
private Set<Invoice> invoices;
public PurchaseOrder() {
invoices = new HashSet<Invoice> ();
}
public Customer getCustomer()
{
return this.customer;
}
public void setCustomer(Customer customer)
{
this.customer = customer;
}
public void addToCustomer(Customer customer)
{
//this IF is important for avoiding an infinite loop
if(!customer.getPurchaseOrders().contains(this))
{
customer.addPurchaseOrder(this);
}
this.customer = customer;
}
public void removeFromCustomer(Customer customer)
{
//this IF is important for avoiding an infinite loop
if(customer.getPurchaseOrders().contains(this))
{
customer.removePurchaseOrder(this);
}
this.customer = null;
}
public Set<Invoice> getInvoices()
{
return this.invoices;
}
public void setInvoices(Set<Invoice> invoices)
{
this.invoices = invoices;
}
public void addInvoice(Invoice invoice)
{
this.invoices.add(invoice);
//this IF is important for avoiding an infinite loop
if (invoice.getPurchaseOrder() != this)
{
invoice.addToPurchaseOrder(this);
}
}
public void removeInvoice(Invoice invoice)
{
this.invoices.remove(invoice);
//this IF is important to avoid an infinite loop
if(invoice.getPurchaseOrder() != null)
{
invoice.removeFromPurchaseOrder(this);
}
}
Invoice
private int id; //#Id and identity column
private String name;
#ManyToOne
#JoinColumn(name="purchaseorder_id")
private PurchaseOrder purchaseOrder;
public Invoice() {
}
public PurchaseOrder getPurchaseOrder()
{
return this.purchaseOrder;
}
public void setPurchaseOrder(PurchaseOrder purchaseOrder)
{
this.purchaseOrder = purchaseOrder;
}
public void addToPurchaseOrder(PurchaseOrder purchaseOrder)
{
//this IF is important for avoiding an infinite loop
if(!purchaseOrder.getInvoices().contains(this))
{
purchaseOrder.addInvoice(this);
}
this.purchaseOrder = purchaseOrder;
}
public void removeFromPurchaseOrder(PurchaseOrder purchaseOrder)
{
//this IF is important for avoiding an infinite loop
if(purchaseOrder.getInvoices().contains(this))
{
purchaseOrder.removeInvoice(this);
}
this.purchaseOrder = null;
}
GenericDao (Parent of all other DAOs)
#Stateful
public class GenericDao<T extends Serializable, PK> implements IGenericDao<T, PK>
{
#PersistenceContext(unitName = "my_PU")
protected EntityManager em;
private Class<T> type;
public Class<T> getType()
{
return type;
}
public void setType(Class<T> type)
{
this.type = type;
}
public void create(T newObject)
{
em.persist(newObject);
}
public T read(PK id)
{
return em.find(type, id);
}
public T update(T transientObject)
{
return em.merge(transientObject);
}
public void delete(T objectToDelete)
{
em.remove(objectToDelete);
}
public T getResultObject(String namedQuery, Map<String, Object> criteria)
throws DatabaseException
{
List<T> records = getResultSetList(namedQuery, criteria);
if(records.isEmpty())
{
return null;
}
else if (records.size() != 1)
{
throw new DatabaseException("Too many records found!");
}
else
{
return records.remove(0);
}
}
}
Controllers
CustomerController
#RequestScoped
public class CustomerController extends FormRequestController
{
#Inject
private HTMLDataTableActionBean htmlDataTableActionBean;
#EJB
private ICustomerDao customerDao;
#Inject
private Customer customer;
#PostConstruct
public void init() throws DatabaseException
{
setEntityObjectList(findAll());
if (null == this.getCustomer())
{
setCustomer(new Customer());
}
}
public void processRequest(FormActionToPerform action) throws DatabaseException
{
switch (action)
{
case SHOW_ADD_VIEW:
setCustomer(new Customer());
break;
case SHOW_VIEW_FOR_LIST:
setEntityObjectList(findAll());
break;
case SHOW_EDIT_VIEW:
case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
setCustomer((Customer) getHtmlDataTableActionBean()
.getSelectedEntityObject());
break;
case SHOW_DELETE_VIEW:
setCustomer((Customer) getHtmlDataTableActionBean()
.getSelectedEntityObject());
delete();
break;
}
}
public String doShowUIView(FormActionToPerform action)
{
String responseURL = "fail.xhtml";
if (null == this.customer)
{
return responseURL;
}
else
{
switch (action)
{
case SHOW_ADD_VIEW:
responseURL = "customer.xhtml";
break;
case SHOW_EDIT_VIEW:
responseURL = "customer.xhtml";
break;
case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
responseURL = "viewCustomer.xhtml";
break;
case SHOW_DELETE_VIEW:
responseURL = "customerList.xhtml";
break;
case SHOW_VIEW_FOR_LIST:
if (this.entityObjectList.size() == 0)
{
setErrorMessage("No customers to display");
}
responseURL = "customerList.xhtml";
break;
default:
responseURL = "index.xhtml";
}
}
return responseURL;
}
public String save()
{
String url = "success.xhtml";
Customer existingCustomer = null;
try
{
existingCustomer =
customerDao.getCustomerByName(this.getCustomer().getName());
if(existingCustomer != null)
{
//there's already a customer with this name, don't make a new one
setErrorMessage("Customer already exists");
url = "fail.xhtml";
}
customerDao.update(customer);
}
catch (DatabaseException e)
{
setErrorMessage(e.toString());
e.printStackTrace();
url = "fail.xhtml";
}
return url;
}
}
POController
#RequestScoped
public class PurchaseOrderController extends FormRequestController
{
#Inject
private HTMLDataTableActionBean htmlDataTableActionBean;
#EJB
private IPurchaseOrderDao purchaseOrderDao;
#EJB
private IInvoiceDao invoiceDao;
#EJB
private ICustomerDao customerDao;
#Inject
private PurchaseOrder purchaseOrder;
private List<SelectItem> customerList;
private String selectedCustomer;
#PostConstruct
public void init() throws DatabaseException
{
setEntityObjectList(findAll());
if (null == purchaseOrder)
{
purchaseOrder = new PurchaseOrder();
setEditMode(false);
}
}
public void processRequest(FormActionToPerform action)
throws DatabaseException
{
switch (action)
{
case SHOW_ADD_VIEW:
setPurchaseOrder(new PurchaseOrder());
break;
case SHOW_VIEW_FOR_LIST:
setEntityObjectList(findAll());
break;
case SHOW_EDIT_VIEW:
case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
{
setPurchaseOrder(
(PurchaseOrder)getHtmlDataTableActionBean().
getSelectedEntityObject());
}
break;
case SHOW_DELETE_VIEW:
{
setPurchaseOrder(
(PurchaseOrder)getHtmlDataTableActionBean().
getSelectedEntityObject());
delete();
}
break;
}
}
String doShowUIView(FormActionToPerform action)
{
String responseURL = "fail.xhtml";
switch (action)
{
case SHOW_ADD_VIEW:
responseURL = "purchaseOrder.xhtml";
break;
case SHOW_EDIT_VIEW:
setEditMode(true);
setComponent(null);
responseURL = "purchaseOrder.xhtml";
break;
case SHOW_DELETE_VIEW:
case SHOW_VIEW_FOR_LIST:
if (this.entityObjectList.size() == 0)
{
setErrorMessage("No orders to display");
}
responseURL = "purchaseOrderList.xhtml";
break;
case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
responseURL = "viewPurchaseOrder.xhtml";
break;
default:
responseURL = HOME;
}
return responseURL;
}
public String save()
{
String responseURL = "fail.xhtml";
try
{
PurchaseOrder dbPurchaseOrder =
purchaseOrderDao.getPurchaseOrderByName(purchaseOrder.getName());
if(dbPurchaseOrder == null)
{
dbPurchaseOrder = purchaseOrder;
}
Customer customer = customerDao.getCustomerByName(selectedCustomer);
dbPurchaseOrder.addToCustomer(customer);
purchaseOrder = purchaseOrderDao.update(dbPurchaseOrder);
//replace the not-yet-persisted dbPurchaseOrder object in customer
//with the persisted purchaseOrderobject returned from the update()
//call above.
customer.removePurchaseOrder(dbPurchaseOrder);
customer.addPurchaseOrder(purchaseOrder);
customerDao.update(customer);
System.out.println("# of Purchase orders for customer: "+
purchaseOrder.getCustomer().getPurchaseOrders().size());
//Output: # of Purchase orders for customer: 1
responseURL = "success.xhtml";
}
catch (DatabaseException e)
{
e.printStackTrace();
setErrorMessage(e.toString());
responseURL = null;
}
return responseURL;
}
}
InvoicesController
#RequestScoped
public class InvoiceController extends FormRequestController
{
#Inject
private HTMLDataTableActionBean htmlDataTableActionBean;
#EJB
private IInvoiceDao invoiceDao;
#Inject
private Invoice invoice;
#EJB
private IPurchaseOrderDao purchaseOrderDao;
private List<SelectItem> purchaseOrderList;
private String selectedPurchaseOrder;
#PostConstruct
public void init() throws DatabaseException
{
setEntityObjectList(findAll());
if (null == invoice)
{
invoice = new Invoice();
setEditMode(false);
}
}
public void processRequest(FormActionToPerform action) throws DatabaseException
{
switch (action)
{
case SHOW_ADD_VIEW:
break;
case SHOW_VIEW_FOR_LIST:
setEntityObjectList(findAll());
break;
case SHOW_EDIT_VIEW:
case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
{
setInvoice((Invoice)getHtmlDataTableActionBean().
getSelectedEntityObject());
}
break;
case SHOW_DELETE_VIEW:
{
setInvoice((Invoice)getHtmlDataTableActionBean().
getSelectedEntityObject());
delete();
}
break;
}
}
String doShowUIView(FormActionToPerform action)
{
String responseUrl = "fail.xhtml";
if (null == invoice)
{
System.out.println("invoice == null");
return responseUrl;
}
else
{
switch (action)
{
case SHOW_ADD_VIEW:
responseUrl = "invoice.xhtml";
break;
case SHOW_EDIT_VIEW:
setEditMode(true);
setComponent(null);
responseUrl = "invoice.xhtml";
break;
case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
responseUrl = "viewInvoice.xhtml";
break;
case SHOW_DELETE_VIEW:
case SHOW_VIEW_FOR_LIST:
if (this.entityObjectList.size() == 0)
{
setErrorMessage("no invoices to display");
}
responseUrl = "invoiceList.xhtml";
break;
default:
responseUrl = "index.xhtml";
}
}
return responseUrl;
}
public String save()
{
String responseUrl = "fail.xhtml";
try
{
Invoice dbInvoice = invoiceDao.getInvoiceByName(invoice.getName());
if(dbInvoice == null)
{
//this is a new invoice
dbInvoice = invoice;
}
PurchaseOrder purchaseOrder =
purchaseOrderDao.getPurchaseOrderByName(selectedPurchaseOrder);
dbInvoice.addToPurchaseOrder(purchaseOrder);
invoice = invoiceDao.update(dbInvoice);
//replace the not-yet-persisted dbInvoice object in purchaseOrder
//with the persisted invoice object returned from the update() call above.
purchaseOrder.removeInvoice(dbInvoice);
purchaseOrder.addInvoice(invoice);
purchaseOrderDao.update(purchaseOrder);
System.out.println("# of Invoices for purchase order: "+
invoice.getPurchaseOrder().getInvoices().size());
//Output: # of Invoices for purchase order: 1
responseUrl = "success.xhtml";
}
catch (Exception e)
{
System.out.println(e.toString());
e.printStackTrace();
setErrorMessage(e.toString());
}
return responseUrl;
}
}
FormRequestController
public abstract class FormRequestController implements NavigationConstants
{
protected enum FormActionToPerform {
SHOW_ADD_VIEW,
SHOW_EDIT_VIEW,
SHOW_DELETE_VIEW,
SHOW_VIEW_TO_VIEW_SELECTED_OBJECT,
SHOW_VIEW_FOR_LIST;
}
protected FacesContext context;
protected List<?> entityObjectList;
private UIComponent component;
protected boolean editMode;
protected String componentId = null;
public String showViewDataTable() throws DatabaseException {
processRequest(FormActionToPerform.SHOW_VIEW_FOR_LIST);
return doShowUIView(FormActionToPerform.SHOW_VIEW_FOR_LIST);
}
public String showViewToAdd() throws DatabaseException {
processRequest(FormActionToPerform.SHOW_ADD_VIEW);
return doShowUIView(FormActionToPerform.SHOW_ADD_VIEW);
}
public String showViewToEdit() throws DatabaseException {
processRequest(FormActionToPerform.SHOW_EDIT_VIEW);
return doShowUIView(FormActionToPerform.SHOW_EDIT_VIEW);
}
public String showViewToDeleteDetails() throws DatabaseException {
processRequest(FormActionToPerform.SHOW_DELETE_VIEW);
return doShowUIView(FormActionToPerform.SHOW_DELETE_VIEW);
}
public String showViewToViewDetails() throws DatabaseException {
processRequest(FormActionToPerform.SHOW_VIEW_TO_VIEW_SELECTED_OBJECT);
return doShowUIView(FormActionToPerform.SHOW_VIEW_TO_VIEW_SELECTED_OBJECT);
}
abstract String doShowUIView(FormActionToPerform action);
abstract void processRequest(FormActionToPerform action) throws DatabaseException;
protected void bindData() {
}
abstract String save();
abstract void delete() throws DatabaseException;
public List<?> getEntityObjectList() {
return entityObjectList;
}
public void setEntityObjectList(List<?> entityObjectList) {
this.entityObjectList = entityObjectList;
}
public FacesContext getContext() {
setContext(FacesContext.getCurrentInstance());
return context;
}
public void setContext(FacesContext context) {
this.context = context;
}
public UIComponent getComponent() {
return component;
}
public void setComponent(UIComponent component) {
this.component = component;
}
}
I'm iterating over the Customer#purchaseOrders and PurchaseOrder#invoices using a h:dataTable. SO keeps misidentifying my JSF code as misformatted code and telling me to indent it as such so I can't show my webpage code but hopefully there's enough above to spot my mistake.
Thanks in advance for your time.
You should not have logic within the set/get methods if JPA is set to use property access. The logic within the set methods will cause JPA to trigger lazy collections etc when building entities and might have other adverse affects depending on the provider internals. I would either switch your annotations so they are on the fields, or remove the
if(!purchaseOrder.getInvoices().contains(this))
{
purchaseOrder.addInvoice(this);
}
logic from the set methods. The application can still use the addInvoice and have it set both sides of the relationship as the addInvoice methods are not used by JPA when loading entities.

JPA, How to find an object that has composite id?

Based on second approach answered here I designed my JPA class.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchKeywordJPA that = (SearchKeywordJPA) o;
if (date != that.date) return false;
if (!keyword.equals(that.keyword)) return false;
if (!userUUID.equals(that.userUUID)) return false;
return true;
}
#Override
public int hashCode() {
int result = keyword.hashCode();
result = 31 * result + (int) (date ^ (date >>> 32));
result = 31 * result + userUUID.hashCode();
return result;
}
#Override
public String toString() {
return "SearchKeywordJPA{" +
"keyword='" + keyword + '\'' +
", date=" + date +
", userUUID='" + userUUID + '\'' +
'}';
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public long getDate() {
return date;
}
public void setDate(long date) {
this.date = date;
}
public String getUserUUID() {
return userUUID;
}
public void setUserUUID(String userUUID) {
this.userUUID = userUUID;
}
#Override
public int compareTo(SearchKeywordJPA searchRecord) {
long comparedDate = searchRecord.date;
if (this.date > comparedDate) {
return 1;
} else if (this.date == comparedDate) {
return 0;
} else {
return -1;
}
}
/**********************
* Key class
**********************/
public class SearchKeyId {
private int id;
private int version;
}
}
In my servlet I want to check datastore and store my object if is not exist.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
for(SearchKeywordJPA item: applicationList) {
if(!isRecorded(item))
storeRecord(item);
}
}
private boolean isRecorded(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
SearchKeywordJPA item = em.find(SearchKeywordJPA.class, record);
return item != null;
}
private void storeRecord(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
em.persist(record);
}
However when I run, application crashes and log says
javax.persistence.PersistenceException: org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of type com.twitterjaya.model.SearchKeywordJPA identified by SearchKeywordJPA{keyword='airasia', date=1335680686149, userUUID='FFFF0000'}. This is not a valid representation of a primary key for an instance of com.twitterjaya.model.SearchKeywordJPA.
What is the reason? any suggestion would be appreciated. Thanks
You pass an instance of the IdClass into em.find ... i.e SearchKeyId. Obviously if you really have an IdClass that has no equals/hashCode/toString/constructor then you will likely get many problems. Those problems will only be increased by using an ancient plugin for GAE/Datastore.
If your Key is
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
you are doing it wrong.
IdClass does not need any annotation of #IdClass just the #Id
annotation.
Key can not be an entity.
Need to implements Serializable , comparable is not needed
Need to override equals and hascode and have no arg constructor
Class key need to be as follows.
public class SearchKeyId implements Serializable {
private String keyword;
private long date;
And your entity I assume something like this.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeyId.class)
public class SearchKeywordJPA {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
Just consider that find method will use the SearchKey.class to find
the entities.
Fields that are in the IdClass need to have #Id annotation in the entity.
Key can not be an entity on its own.
Comparable is not really needed as all the comparison are placed in the IdClass

EntityManager persist() method does not insert record to database => SEVERE: javax.persistence.TransactionRequiredException

I have problem with using EntityManager persist() method.
I am using JSF2.0, glassfish 3, JPA and hibernate, i am not using spring.
I try to add events in an events table with the method persist, but EntityManager persist() method does not insert record to database and i have this error message =>
SEVERE: javax.persistence.TransactionRequiredException
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTxRequiredCheck(EntityManagerWrapper.java:163)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.flush(EntityManagerWrapper.java:411)
at dao.EvenementDao.addEvenement(EvenementDao.java:128).
#ManagedBean
#Stateless
public class EvenementDao implements Serializable{
/**
*
*/
private static final long serialVersionUID = -3343483942392617877L;
/**
*
*/
private List<TEvenement> listeEvenement;
private List<SelectItem> listeSelectItemEvnt;
private TEvenement tevenement ;
public EvenementDao() {
}
#PersistenceUnit(unitName="GA2010-ejbPU-dev")
private EntityManagerFactory emf;
#PostConstruct
private void init() {
tevenement = new TEvenement();
}
public List<TEvenement> getListeEvenement() {
EntityManager em = emf.createEntityManager();
TypedQuery<TEvenement> requete = m.createNamedQuery("TEvenement.findPrivateOther",
TEvenement.class);
listeEvenement = requete.getResultList();
return listeEvenement;
}
public TEvenement getEvenement() {
return tevenement;
}
public void setEvenement(TEvenement evenement) {
this.tevenement = evenement;
}
public void addEvenement(){
EntityManager em = emf.createEntityManager();
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().
getExternalContext().getSession(false);
Integer codeUser = (Integer) session.getAttribute("codeUser");
tevenement.setUtilCreation(codeUser);
System.out.println("je rentre dans addevenemnt");
try{
System.out.println("i persist "+ em);
em.persist(tevenement);
em.flush();
System.out.println(tevenement.getDetailsEvenement());
FacesMessage message = new FacesMessage("Evenement ajouté avec succès.");
FacesContext.getCurrentInstance().addMessage(null, message);
}catch(Exception e){
e.printStackTrace();
}
}
}
So, this is not working, but the progam enter in the fonction addEvenement , FacesMessage message = new FacesMessage("Evenement ajouté avec succès."); returns me the message as if it was working.
i thouht it was due to my entityMAnager but in fact an ohter function works fine with the same Entity manager so i dont understand.
**public List<TEvenement> getListeEvenement()** {
EntityManager em = emf.createEntityManager();
TypedQuery<TEvenement> requete = em.createNamedQuery("TEvenement.findPrivateOther", TEvenement.class);
listeEvenement = requete.getResultList();
return listeEvenement;
}
this one works fine, the only difference is that in this case the query is a select and in the other case it's a persist so a query that impact the database.
the code of the entity :
#Entity
#Table(name="t_evenements")
#NamedQueries({#NamedQuery(name="TEvenement.findAll", query="SELECT evnt FROM TEvenement evnt"),
#NamedQuery(name="TEvenement.findPrivateOther", query="SELECT evnt FROM TEvenement evnt WHERE evnt.typeEvenement = 6 OR evnt.typeEvenement = 7")})
public class TEvenement implements Serializable {
private static final long serialVersionUID = -1908959833491896991L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="REF_EVENEMENT", unique=true, nullable=false)
private Integer refEvenement;
#Temporal(TemporalType.DATE)
#Column(name="DATE_DEBUT_EVENEMENT")
private Date dateDebutEvenement;
#Temporal(TemporalType.DATE)
#Column(name="DATE_FIN_EVENEMENT")
private Date dateFinEvenement;
#Column(name="DETAILS_EVENEMENT")
private String detailsEvenement;
#Column(name="IS_EVERYDAY")
private byte isEveryday;
#Column(name="RAPPEL_EVENEMENT")
private int rappelEvenement;
public Integer getUtilEvenement() {
return utilEvenement;
}
public void setUtilEvenement(Integer utilEvenement) {
this.utilEvenement = utilEvenement;
}
#Column(name="TITRE_EVENEMENT")
private String titreEvenement;
#Column(name="TYPE_EVENEMENT")
private String typeEvenement;
#Column(name="UTIL_COPIE_EVENEMENT")
private Integer utilCopieEvenement;
#Column(name="UTIL_EVENEMENT")
private Integer utilEvenement;
#Column(name="HEURE_EVENEMENT")
private String heureEvenement;
#Column(name="UTIL_CREATION")
private Integer utilCreation;
public String getHeureEvenement() {
return heureEvenement;
}
public void setHeureEvenement(String heureEvenement) {
this.heureEvenement = heureEvenement;
}
public TEvenement() {
}
public Integer getRefEvenement() {
return this.refEvenement;
}
public void setRefEvenement(int refEvenement) {
this.refEvenement = refEvenement;
}
public Date getDateDebutEvenement() {
return this.dateDebutEvenement;
}
public Integer getUtilCreation() {
return utilCreation;
}
public void setUtilCreation(Integer utilCreation) {
this.utilCreation = utilCreation;
}
public void setUtilCopieEvenement(Integer utilCopieEvenement) {
this.utilCopieEvenement = utilCopieEvenement;
}
public void setDateDebutEvenement(Date dateDebutEvenement) {
this.dateDebutEvenement = dateDebutEvenement;
}
public Date getDateFinEvenement() {
return this.dateFinEvenement;
}
public void setDateFinEvenement(Date dateFinEvenement) {
this.dateFinEvenement = dateFinEvenement;
}
public String getDetailsEvenement() {
return this.detailsEvenement;
}
public void setDetailsEvenement(String detailsEvenement) {
this.detailsEvenement = detailsEvenement;
}
public byte getIsEveryday() {
return this.isEveryday;
}
public void setIsEveryday(byte isEveryday) {
this.isEveryday = isEveryday;
}
public int getRappelEvenement() {
return this.rappelEvenement;
}
public void setRappelEvenement(int rappelEvenement) {
this.rappelEvenement = rappelEvenement;
}
public String getTitreEvenement() {
return this.titreEvenement;
}
public void setTitreEvenement(String titreEvenement) {
this.titreEvenement = titreEvenement;
}
public String getTypeEvenement() {
return this.typeEvenement;
}
public void setTypeEvenement(String typeEvenement) {
this.typeEvenement = typeEvenement;
}
public Integer getUtilCopieEvenement() {
return this.utilCopieEvenement;
}
public void setUtilCopieEvenement(int utilCopieEvenement) {
this.utilCopieEvenement = utilCopieEvenement;
}
}
Do anyone have a idea what am i missing?
The difference isn't that you call persist, the difference is that you call em.flush() which as the error states, requires the EntityManager be joined to a transaction. Makes sure your getListeEvenement() method is wrapped in a transaction, or start one depending on your setup.

In Spring-mvc the attribute names in view have to always match the property names in model?

In the http request body, the way password string is passed is "pass=1111", however in the bean the way password is defined is ''private String password". Is there a way I can use annotation to handle the difference or I have to always match names?
The Http request is like this
curl -H "Accept:text/html" -H "Content-Type application/x-www-form-urlencoded" -d 'email=test%40gmail.com&pass=1111&passconfirm=1111&name=x+y' "http://localhost:8080/project/register"
Handler method is
#RequestMapping(method = RequestMethod.POST, headers = "content-type=application/x-www-form-urlencoded")
public String register(#ModelAttribute UserAccountBean account) ...
UserAccountBean is
public class UserAccountBean2 {
#NotNull
#Size(min = 1, max = 25)
private String name;
#NotNull
#Size(min = 4, max = 8)
private String password;
#NotNull
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String toString() {
return new ToStringCreator(this).append("name", name).append("password", password).toString();
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Use #RequestParam annotation in #InitBinder annotated method, and set the desired value manually.
UserController
#InitBinder(value="user")
public void bind(WebDataBinder dataBinder, WebRequest webRequest, #RequestParam(value="pass", required=false) String password) {
User user = (User) dataBinder.getTarget();
user.setPassword(password);
}
Is there a way I can use annotation to
handle the difference or I have to
always match names?
AFAIK there is no ready-made annotation in Spring MVC that can resolve your problem; you need custom setup to handle the situation.
WebModelAttribute
#Target({ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface WebModelAttribute {
String modelAttributeName();
WebParameterMapping[] parameterMappings();
}
WebParameterMapping
#Target({ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface WebParameterMapping {
String webProperty();
String beanProperty();
}
UserController
#Controller
public class UserController extends AbstractController {
#Override
#InitBinder(value="user")
#WebModelAttribute(modelAttributeName="user", parameterMappings={#WebParameterMapping(webProperty="pass", beanProperty="password")})
protected void bindWebParameters(WebDataBinder dataBinder, WebRequest webRequest, WebParameterResolver mappingResolver) {
super.bindWebParameters(dataBinder, webRequest, mappingResolver);
}
AbstractController
public class AbstractController {
protected void bindWebParameters(WebDataBinder dataBinder, WebRequest webRequest, WebParameterResolver mappingResolver) {
if(mappingResolver != null && dataBinder.getTarget() != null && dataBinder.getObjectName().equals(mappingResolver.getModelAttributeName())) {
String[] allowedFields = mappingResolver.getAllowedFields(dataBinder.getAllowedFields());
String[] disallowedFields = mappingResolver.getDisallowedFields(dataBinder.getDisallowedFields());
dataBinder.setAllowedFields(allowedFields);
dataBinder.setDisallowedFields(disallowedFields);
dataBinder.bind(mappingResolver.getPropertyValues(dataBinder, webRequest));
}
}
}
WebParameterResolver
public class WebParameterResolver {
private String modelAttributeName;
private WebParameterMapping[] parameterMappings;
public WebParameterResolver(String modelAttributeName,
WebParameterMapping[] parameterMappings) {
this.modelAttributeName = modelAttributeName;
this.parameterMappings = parameterMappings;
}
public String getModelAttributeName() {
return modelAttributeName;
}
public String[] getDisallowedFields(String[] existingDisallowedFields) {
List<String> disallowedFields = new ArrayList<String>();
for (WebParameterMapping parameterMapping : parameterMappings) {
disallowedFields.add(parameterMapping.webProperty());
}
if (existingDisallowedFields != null) {
for (String disallowedField : existingDisallowedFields) {
disallowedFields.add(disallowedField);
}
}
return disallowedFields.toArray(new String[disallowedFields.size()]);
}
public String[] getAllowedFields(String[] existingAllowedFields) {
List<String> allowedFields = new ArrayList<String>();
for (WebParameterMapping parameterMapping : parameterMappings) {
allowedFields.add(parameterMapping.beanProperty());
}
if (existingAllowedFields != null) {
for (String allowedField : existingAllowedFields) {
allowedFields.add(allowedField);
}
}
return allowedFields.toArray(new String[allowedFields.size()]);
}
public MutablePropertyValues getPropertyValues(WebDataBinder dataBinder,
WebRequest webRequest) {
MutablePropertyValues propertyValues = new MutablePropertyValues();
for (WebParameterMapping parameterMapping : parameterMappings) {
String[] values = webRequest.getParameterValues(parameterMapping.webProperty());
if (values == null || values.length == 0) {
// do nothing
} else if (values.length == 1) {
propertyValues.add(parameterMapping.beanProperty(), values[0]);
} else {
propertyValues.add(parameterMapping.beanProperty(), values);
}
}
dataBinder.bind(propertyValues);
return propertyValues;
}
}
CustomArgumentResolver
public class CustomArgumentResolver implements WebArgumentResolver {
#Override
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
if(methodParameter.getParameterType().equals(WebParameterResolver.class)) {
WebModelAttribute webModelAttribute = methodParameter.getMethod().getAnnotation(WebModelAttribute.class);
if(webModelAttribute == null) {
throw new RuntimeException("method must have WebModelAttribute");
}
return new WebParameterResolver(webModelAttribute.modelAttributeName(), webModelAttribute.parameterMappings());
}
return UNRESOLVED;
}
}
beans.xml
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolvers" ref="timetracking.annotations.CustomArgumentResolver"/>
</bean>
<bean name="timetracking.annotations.CustomArgumentResolver"
class="timetracking.annotations.CustomArgumentResolver" />
You can also have a public static void bindWebParameters(...) method in some helper class; so you don't have to extend the AbstractController every time.
You can achieve it with this:
#RequestMapping(method = RequestMethod.POST, headers = "content-type=application/x-www-form-urlencoded")
public String register(#ModelAttribute("userAccountBean") UserAccountBean account) ...
#ModelAttribute("userAccountBean")
public UserAccountBean getUserAccountBean(HttpServletRequest req) {
UserAccountBean uab = new UserAccountBean();
uab.setPassword(req.getParameter("pass"));
return uab;
}
There is no annotation based solution in 3.0.
Just provide additional getPass() setPass(String pass) method and you should be set.