JPA/EclipseLink - Error using multiple JpaEntityManagers - jpa

We are trying to set up an application running on a Glassfish 4 Server. For data retrieval, we are using JPA 2.0 (EclipseLink Kepler). For several reasons, additionally to the normal user management, we also want users to authorize via the DMBS user management.
In order to accomplish that, we create a user-specific JpaEntityManager when the user logs in (In order to check the login itself, we also have a general server-side JpaEntityManager, whose DBMS credentials are located in the persistence.xml). Some code here:
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.jpa.JpaHelper;
import application.auth.general.Credentials;
import application.config.Config;
import application.data.model.JpaSession;
import application.logger.Logger;
/**
*/
public class EntityManagerBase {
/**
* Singleton instance
*/
private static EntityManagerBase instance = new EntityManagerBase();
/**
* Hashtable for storage of user specific EntityManagers
*
* #param String userName
* #param EntityManager corresponding EntityManager
*/
private Hashtable<Integer, JpaEntityManager> jpaEms = new Hashtable<>();
/**
* Default constructor for singleton, creates single jpaEm instance for
* user ID -1, rights are defined in server-side persistence.xml
*/
private EntityManagerBase() {
String persistenceUnitName = Config.get("pvapp.data.persistence.unitName");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);
EntityManager em = emf.createEntityManager();
JpaEntityManager jpaEm = JpaHelper.getEntityManager(em);
jpaEms.put(-1, jpaEm);
}
public static EntityManagerBase getInstance() {
return instance;
}
/**
* Prevent cloning of singleton instance
*/
#Override
protected Object clone() throws CloneNotSupportedException {
String name = this.getClass().getCanonicalName();
throw new CloneNotSupportedException(name
+ " does not support clone(). Use " + name
+ ".getInstance() instead.");
}
public void createJpaEntityManager(JpaSession session, Credentials credentials) {
String persistenceUnitName = Config.get("pvapp.data.persistence.unitName");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);
HashMap<String, String> properties = new HashMap<>();
properties.put("javax.persistence.jdbc.user", credentials.getUserName());
properties.put("javax.persistence.jdbc.password", credentials.getPassword());
EntityManager em = emf.createEntityManager(properties);
JpaEntityManager jpaEm = JpaHelper.getEntityManager(em);
jpaEms.put(session.getUser().getId(), jpaEm);
}
public JpaEntityManager getJpaEntityManager(JpaSession session) {
return this.jpaEms.get(session.getUser().getId());
}
/**
* Get a JPA entity manager for a numeric user id
*
* #param id
* #return
*/
public JpaEntityManager getJpaEntityManager(int id) {
return this.jpaEms.get(id);
}
}
So, when a user logs in, createJpaEntityManager is being called, the newly created JpaEntityManager is being stored in a Hashtable with his userId. This works fine for retrieving default POJOs, but if we have relations to other objects, like that:
import java.io.Serializable;
import java.util.List;
import javax.persistence.Column;
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;
import javax.xml.bind.annotation.XmlRootElement;
import application.auth.general.Authorizable;
import static javax.persistence.FetchType.EAGER;
/**
* The persistent class for the modulesignature database table.
*
*/
#Entity(name="JpaModuleSignature")
#Table(name="modulesignature")
#XmlRootElement
#NamedQueries({
#NamedQuery(name="JpaModuleSignature.findAll", query="SELECT m FROM JpaModuleSignature m"),
#NamedQuery(name="JpaModuleSignature.findById", query="SELECT m FROM JpaModuleSignature m WHERE m.id = :id")
})
public class JpaModuleSignature implements Serializable, Authorizable, JpaRecord {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="authObjId")
private int authObjId;
#Column(name="className")
private String className;
#Column(name="version")
private int version;
//bi-directional many-to-one association to JpaMenuItem
#OneToMany(mappedBy="moduleSignature", fetch = EAGER)
private List<JpaMenuItem> menuItems;
public JpaModuleSignature() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public int getAuthObjId() {
return this.authObjId;
}
public void setAuthObjId(int authObjId) {
this.authObjId = authObjId;
}
public String getClassName() {
return this.className;
}
public void setClassName(String className) {
this.className = className;
}
public int getVersion() {
return this.version;
}
public void setVersion(int version) {
this.version = version;
}
public List<JpaMenuItem> getMenuItems() {
return this.menuItems;
}
public void setMenuItems(List<JpaMenuItem> menuItems) {
this.menuItems = menuItems;
}
public JpaMenuItem addMenuItem(JpaMenuItem menuItem) {
getMenuItems().add(menuItem);
menuItem.setModuleSignature(this);
return menuItem;
}
public JpaMenuItem removeMenuItem(JpaMenuItem menuItem) {
getMenuItems().remove(menuItem);
menuItem.setModuleSignature(null);
return menuItem;
}
}
Let's say we proceed as follows - determine the user's id and fetch the correct JpaEntityManager (unfortunately, some more code is necessary for that):
First the Jersey 2.0-Webservice, which is rather unspectacular:
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import application.data.model.JpaMenuItem;
import application.data.model.JpaModuleSignature;
import application.data.model.JpaSession;
import application.logger.Logger;
import application.server.storage.GenericStorage;
/**
*/
#Provider
#Path("JpaModuleSignature")
public class ModuleSignatureService extends AbstractService {
/**
* Storage unit dealing with storage of the JPA entities
*/
//private ModuleSignatureStorage storage = ModuleSignatureStorage.getInstance();
private GenericStorage<JpaModuleSignature> storage =
GenericStorage.createInstance(JpaModuleSignature.class);
/**
* Include application.data.model classes
*/
public ModuleSignatureService() {
super();
}
/**
* Get a list of all ModuleSignatures in the database
*
* #return list of ModuleSignatures
*/
#Produces(MediaType.APPLICATION_XML)
#Path("list")
#GET
public JpaModuleSignature[] getModuleSignatures(#QueryParam("sessionId") String sessionId) {
Logger.getInstance().setVerbosity((byte) 3);
JpaSession session = this.getSession(sessionId);
List<JpaModuleSignature> ms = storage.getList(session);
Logger.getInstance().log("-----3-----");
for (int i = 0; i < ms.size(); i++) {
Logger.getInstance().log("signature #" + i + ": " + ms.get(i).getTitle());
List<JpaMenuItem> menuItems = ms.get(i).getMenuItems();
for (int j = 0; j < menuItems.size(); j++) {
Logger.getInstance().log("menu item #" + i + "-" + j + ": " + menuItems.get(j));
}
}
Logger.getInstance().log("-----4-----");
JpaModuleSignature ret[] = new JpaModuleSignature[0];
return ms.toArray(ret);
}
}
There's a callback to our generic storage, which contains the call to the user-specific JpaEntityManager (just look at the one line in getList where StorageBase is called statically:
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Query;
import org.eclipse.persistence.jpa.JpaEntityManager;
import application.auth.general.Authorizable;
import application.data.model.JpaRecord;
import application.data.model.JpaSession;
import application.logger.Logger;
import application.server.auth.AuthorizationManager;
public class GenericStorage<T> extends StorageBase {
/**
* This is an internal variable in order to store the class
* of the generic object. It is used as a helper later in the code
*/
private final Class<T> storageClass;
/**
* The constructor has only "protected" visibility so the formal type
* parameter has to be specified via createClient
*
* #param clientClass
*/
protected GenericStorage(Class<T> storageClass) {
this.storageClass = storageClass;
}
/**
* Static method for creating instances of GenericStorage.
*
* #param className
* #return
*/
public static <U> GenericStorage<U> createInstance(Class<U> className) {
return new GenericStorage<U>(className);
}
/**
* Get a list of all items
*
* #return
*/
public List<T> getList(JpaSession session) {
return this.getList(session, null);
}
public List<T> getList(JpaSession session, Hashtable<String,Object> parameters) {
Entity e = (Entity) this.storageClass.getAnnotation(Entity.class);
String entityName = e.name();
String queryString = "SELECT r FROM " + entityName + " r";
if (parameters != null && parameters.size() > 0) {
String where = " WHERE ";
Set<String> paramKeys = parameters.keySet();
Iterator<String> i = paramKeys.iterator();
while (i.hasNext()) {
String key = i.next();
where += key + " = :" + key;
}
queryString += where;
}
// GET USER-SPECIFIC JpaEntityManager HERE:
JpaEntityManager jpaEm = StorageBase.getJpaEM(session);
Query query = jpaEm.createQuery(queryString);
Logger.getInstance().log("-----1-----");
if (parameters != null && parameters.size() > 0) {
Set<String> paramKeys = parameters.keySet();
Iterator<String> i = paramKeys.iterator();
while (i.hasNext()) {
String key = i.next();
query.setParameter(key, parameters.get(key));
}
}
List<T> L = (List<T>) query.getResultList();
Logger.getInstance().log("-----2----- (" + entityName + ")");
return L;
}
}
Code for StorageBase:
import org.eclipse.persistence.jpa.JpaEntityManager;
import application.data.model.JpaSession;
/**
* Implements general functionality for retrieval of
* user specific JpaEntityManager instances using the
* EntityManagerBase
*/
abstract class StorageBase {
public static JpaEntityManager getJpaEM(JpaSession session) {
return EntityManagerBase.getInstance().getJpaEntityManager(session);
}
public static JpaEntityManager getJpaEM(int id) {
return EntityManagerBase.getInstance().getJpaEntityManager(id);
}
}
If there are any questions what "AbstractService" does - not really necessary to read, it just calls Jersey's 'this.packages(..)' and provides a method to get a JPA Session object by the user's provided session id.
The problem is, that, if we run getModuleSignatures of the ModuleSignatureService, something odd happens: The JpaModuleSignature can basically be retrieved via the correct JpaEntityManager, but when trying to access the linked attribute "menuItems", we get the following error:
INFO: [EL Severe]: 2013-10-09 16:50:24.34--ServerSession(1625769026)--Exception [EclispseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipe.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Access denied for user 'PERSISTENCE.XML-USER'#'localhost' (using password: YES)
Error Code: 1045
Of course the real username is not PERSISTENCE.XML-USER - but it is the one that has been defined in the persistence.xml. We double-checked that the original records are being fetched by the correct user that authorized against the datebase system. However, JPA apparently tries to fetch the linked records via the wrong JpaEntityManager.
Can anyone explain if there is some error on our side, if that is a known problem, or something else that might help?
Thanks in advance.

EMFs wrap shared resources, so as long as the EMF context exists for this persistence unit, the base EMF is used with the base login properties. You will need to setup EclipseLink so that it uses exclusive connections rather than shared connections as described here http://wiki.eclipse.org/EclipseLink/Examples/JPA/Auditing

Related

Why does spring jpa ignore javax.persistence.FetchType.LAZY and javax.persistence.NamedEntityGraph?

I have a basic "Department"/"Employee" example.
Full example here (in master branch) : https://github.com/granadacoder/jpa-simple-example-one.git
If you setup 4 environment variables (listed in the README), the code is runnable/debuggable.
For my "findAll()" method, I am trying to only bring back the scalars of the Department entity. (key and name). Aka, I do NOT want any child Employees to be tacked onto the Department .. when I do a findAll().
I have tried using an name EntityGraph, but it is not working (I still get the full object graph)
I am using FetchType.LAZY as well. (which should have been enough IMHO)... And I am not calling (department) .getEmployees at all.
But I took the extra step of defining and using "departmentJustScalarsEntityGraphName".
#EntityGraph("departmentJustScalarsEntityGraphName")
List<Department> findAll();
The above is loading the entire graph (all department scalar AND the employees) and doing it in an N+1 manner. :(
The #EntityGraph should only be loading the key and name for the findAll() method.
If you find the code comment
/* right here, desperately hoping for each Department in the "entities" to NOT have employees hydrated */
you can put the debugger there and see the issue.
Note, that when I do a "find by single" (findById(key) or findDepartmentByDepartmentNameEquals(name))......I DO want the employees. So answers that broad stroke disassociate the Employees are not ideal.
No matter what I've tried, I'm getting the N+1 issue. (The sample seed data has three Departments.)
Hibernate: select department0_.DepartmentKey as departme1_0_, department0_.CreateOffsetDateTime as createof2_0_, department0_.DepartmentName as departme3_0_ from DepartmentTable department0_
Hibernate: select employees0_.DepartmentForeignKey as departme6_1_0_, employees0_.EmployeeKey as employee1_1_0_, employees0_.EmployeeKey as employee1_1_1_, employees0_.CreateOffsetDateTime as createof2_1_1_, employees0_.FirstName as firstnam3_1_1_, employees0_.LastName as lastname4_1_1_, employees0_.DepartmentForeignKey as departme6_1_1_, employees0_.Ssn as ssn5_1_1_ from EmployeeTable employees0_ where employees0_.DepartmentForeignKey=?
Hibernate: select employees0_.DepartmentForeignKey as departme6_1_0_, employees0_.EmployeeKey as employee1_1_0_, employees0_.EmployeeKey as employee1_1_1_, employees0_.CreateOffsetDateTime as createof2_1_1_, employees0_.FirstName as firstnam3_1_1_, employees0_.LastName as lastname4_1_1_, employees0_.DepartmentForeignKey as departme6_1_1_, employees0_.Ssn as ssn5_1_1_ from EmployeeTable employees0_ where employees0_.DepartmentForeignKey=?
Hibernate: select employees0_.DepartmentForeignKey as departme6_1_0_, employees0_.EmployeeKey as employee1_1_0_, employees0_.EmployeeKey as employee1_1_1_, employees0_.CreateOffsetDateTime as createof2_1_1_, employees0_.FirstName as firstnam3_1_1_, employees0_.LastName as lastname4_1_1_, employees0_.DepartmentForeignKey as departme6_1_1_, employees0_.Ssn as ssn5_1_1_ from EmployeeTable employees0_ where employees0_.DepartmentForeignKey=?
Here are the main code components:
Department.java
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.mycompany.organizationdemo.domain.constants.OrmConstants;
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.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.LinkedHashSet;
import java.util.Set;
#Entity
#NamedEntityGraphs({
#NamedEntityGraph(name = "departmentJustScalarsEntityGraphName", attributeNodes = {
#NamedAttributeNode("departmentKey"),
#NamedAttributeNode("departmentName")})
})
#Table(name = "DepartmentTable")
public class Department implements Serializable {
#Id
#Column(name = "DepartmentKey", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private long departmentKey;
#Column(name = "DepartmentName", unique = true)
private String departmentName;
#Column(name = "CreateOffsetDateTime", columnDefinition = OrmConstants.OffsetDateTimeColumnDefinition)
private OffsetDateTime createOffsetDateTime;
//region Navigation
#OneToMany(
mappedBy = "parentDepartment",
cascade = CascadeType.REMOVE,
orphanRemoval = true,
fetch = FetchType.LAZY /* Lazy or Eager here */
)
private Set<Employee> employees = new LinkedHashSet<>();
//endregion
public long getDepartmentKey() {
return departmentKey;
}
public void setDepartmentKey(long departmentKey) {
this.departmentKey = departmentKey;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
public OffsetDateTime getCreateOffsetDateTime() {
return createOffsetDateTime;
}
public void setCreateOffsetDateTime(OffsetDateTime createOffsetDateTime) {
this.createOffsetDateTime = createOffsetDateTime;
}
//region Navigation
public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
//endregion
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Department that = (Department) o;
return new org.apache.commons.lang3.builder.EqualsBuilder()
.append(departmentKey, that.departmentKey)
.append(departmentName, that.departmentName)
.isEquals();
}
#Override
public int hashCode() {
return new org.apache.commons.lang3.builder.HashCodeBuilder(17, 37)
.append(departmentKey)
.append(departmentName)
.toHashCode();
}
}
Employee.java
import com.mycompany.organizationdemo.domain.constants.OrmConstants;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
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.ManyToOne;
import javax.persistence.Table;
import java.io.Serializable;
import java.time.OffsetDateTime;
#Entity
#Table(name = "EmployeeTable")
public class Employee implements Serializable {
#Id
#Column(name = "EmployeeKey", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private long employeeKey;
#Column(name = "Ssn")
private String ssn;
#Column(name = "LastName")
private String lastName;
#Column(name = "FirstName")
private String firstName;
#Column(name = "CreateOffsetDateTime", columnDefinition = OrmConstants.OffsetDateTimeColumnDefinition)
private OffsetDateTime createOffsetDateTime;
//region Navigation
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Department.class)//, cascade = CascadeType.REMOVE)
#JoinColumn(name = "DepartmentForeignKey")
private Department parentDepartment;
//endregion
public long getEmployeeKey() {
return employeeKey;
}
public void setEmployeeKey(long departmentKey) {
this.employeeKey = departmentKey;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public OffsetDateTime getCreateOffsetDateTime() {
return createOffsetDateTime;
}
public void setCreateOffsetDateTime(OffsetDateTime createOffsetDateTime) {
this.createOffsetDateTime = createOffsetDateTime;
}
//region Navigation
public Department getParentDepartment() {
return parentDepartment;
}
public void setParentDepartment(Department parentDepartment) {
this.parentDepartment = parentDepartment;
}
//endregion
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return new EqualsBuilder()
.append(employeeKey, employee.employeeKey)
.append(ssn, employee.ssn)
.isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(employeeKey)
.append(ssn)
.toHashCode();
}
}
Spring JPA
import com.mycompany.organizationdemo.domain.entities.Department;
import com.mycompany.organizationdemo.domaindatalayer.interfaces.IDepartmentRepository;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import javax.transaction.Transactional;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface DepartmentJpaRepository extends JpaRepository<Department, Long>, IDepartmentRepository {
#EntityGraph("departmentJustScalarsEntityGraphName")
List<Department> findAll();
#Query("SELECT d FROM Department d LEFT JOIN FETCH d.employees WHERE d.departmentName = :departmentName") /* this works because departmentName is a UNIQUE constraint...otherwise it might give back duplicate parents (Departments) */
Optional<Department> findDepartmentByDepartmentNameEquals(#Param("departmentName") String departmentName);
/* note the below, this is "lookup strategy". see https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods */
Collection<Department> findByCreateOffsetDateTimeBefore(OffsetDateTime zdt);
//#Query("SELECT d FROM Department d LEFT JOIN FETCH d.employees WHERE d.departmentKey IN ?1") /* here a Query will bring back repeat parent (Department) rows */
#EntityGraph(attributePaths = {"employees"})
Collection<Department> findDepartmentByDepartmentKeyIn(Set<Long> departmentKeys);
#Modifying
#Transactional
int deleteDepartmentByDepartmentKey(long departmentKey); /* suffers from N+1 problem */
}
and the repository (plain jane) interface
import com.mycompany.organizationdemo.domain.entities.Department;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface IDepartmentRepository {
List<Department> findAll();
Optional<Department> findById(long key);
Optional<Department> findDepartmentByDepartmentNameEquals(String departmentName);
Collection<Department> findByCreateOffsetDateTimeBefore(OffsetDateTime zdt);
Collection<Department> findDepartmentByDepartmentKeyIn(Set<Long> departmentKeys);
Department save(Department item);
int deleteDepartmentByDepartmentKey(long departmentKey);
}
I have found this:
Spring Data JPARepository: How to conditionally fetch children entites
and this:
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm
Spring Boot is fairly new. (from gradle settings in the project)
spring_plugin_version = '2.2.6.RELEASE'
springBootVersion = '2.2.6.RELEASE'
slf4jVersion = "1.7.25"
javaxInjectVersion = "1"
javaxPersistenceApiVersion = "2.2"
junitVersion = "4.12"
mockitoVersion = "3.3.0"
jacksonAnnotationsVersion = "2.11.0"
modelMapperVersion = "2.3.7"
commonsLangVersion = '3.7'
PS
I'm also using the "convert to Dto" trick (as seen here) : https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application but the Department(orm-entity) is already hydrated by that point. :(

Transaction aborted in controller

I'm having troubles implementing JPA methods in my WebApp built in Java ee 7, its returned me "EJBException : Transaction aborted" , but the parameters "ID" and "Description" had send correctly.
package entities;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
*
* #author Fede-Frost
*/
#Entity
#Table(name = "especialidad")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Especialidad.findAll", query = "SELECT e FROM Especialidad e")
, #NamedQuery(name = "Especialidad.findById", query = "SELECT e FROM Especialidad e WHERE e.id = :id")
, #NamedQuery(name = "Especialidad.findByDesc", query = "SELECT e FROM Especialidad e WHERE e.desc = :desc")})
public class Especialidad implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Integer id;
#Size(max = 45)
#Column(name = "desc")
private String desc;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "especialidad")
private List<Medico> medicoList;
public Especialidad() {
}
public Especialidad(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
#XmlTransient
public List<Medico> getMedicoList() {
return medicoList;
}
public void setMedicoList(List<Medico> medicoList) {
this.medicoList = medicoList;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Especialidad)) {
return false;
}
Especialidad other = (Especialidad) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "MyHospital.Especialidad[ id=" + id + " ]";
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package model;
import entities.Estudiolab;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
*
* #author Fede-Frost
*/
#Stateless
public class EstudiolabFacade extends AbstractFacade<Estudiolab> implements EstudiolabFacadeLocal {
#PersistenceContext(unitName = "MyHospital-ejbPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public EstudiolabFacade() {
super(Estudiolab.class);
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package model;
import entities.Especialidad;
import java.util.List;
import javax.ejb.Local;
/**
*
* #author Fede-Frost
*/
#Local
public interface EspecialidadFacadeLocal {
void create(Especialidad especialidad);
void edit(Especialidad especialidad);
void remove(Especialidad especialidad);
Especialidad find(Object id);
List<Especialidad> findAll();
List<Especialidad> findRange(int[] range);
int count();
}
And here it's the big deal, on the controller
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package controller;
import entities.Especialidad;
import javax.inject.Named;
import java.io.Serializable;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.faces.application.FacesMessage;
import javax.faces.view.ViewScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import model.EspecialidadFacadeLocal;
/**
*
* #author Fede-Frost
*/
#ViewScoped
#Named(value = "mediCon")
public class mediCon implements Serializable {
#EJB
private EspecialidadFacadeLocal especFac;
#Inject
private Especialidad espec;
/**
* Creates a new instance of MediCon
* #return
*/
public void addSpec() {
try {
especFac.create(espec);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Exito", "Se registro correctamente"));
} catch (EJBException e) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Aviso", e.toString() ));
}
}
public Especialidad getEspec() {
return espec;
}
public void setEspec(Especialidad espec) {
this.espec = espec;
}
}
As I said, JSF sent me the correct parameters which I tested with FacesContext.addMessage, but I don't know why it doesn't work, thanks in advance.
If a SystemException(A system exception must be a subclass
of a RuntimeException or java.rmi.RemoteException) is thrown, the current transaction will be rollbacked
For more information about the Exception types in EJB, you could have a look at this link.
but the query it's from JPA standard created on especialidadFacade
with especialidaEntity
please have a look at JPA: Weird error when I try to persist an object
in your case, "desc" is a reserved keyword

404 Not Found error while consuming REST on EJB

I developed an messaging EJB application with its services that uses WILDFLY 9 server.
Now the application is working and the database is fully functional, I am trying to develop a REST web-service for this app. So I created my RestActivator :
package tn.carmate.webservice;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("rest")
public class RestActivator extends Application {
}
and my messageRessources.java as follows
package tn.carmate.webservice;
import java.util.List;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import tn.esprit.pi.Services.Services;
import tn.esprit.pi.persistance.Message;
#Path("message")
#RequestScoped
public class MessageRessource {
#EJB
Services servicemessage;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{idreceiver}")
public Response getMessageList(#QueryParam(value = "idreceiver") int idReceiver) {
List<Message> message = servicemessage.getMessageList(idReceiver);
return Response.status(Status.OK).entity(message).build();
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("{idSrc/idDest/content}")
public Response sendMessage(#QueryParam(value = "idSrc") int idSrc, #QueryParam(value = "idDest") int idDest,
#QueryParam(value = "content") String content) {
servicemessage.sendMessage(idSrc, idDest, content);
return Response.status(Status.OK).build();
}
#DELETE
#Path("{idm}")
public Response deleteMessage(#QueryParam(value = "idm") int id) {
servicemessage.deleteMessage(id);
return Response.status(Status.OK).build();
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{idSender/idReceiver}")
public Response getConversation(#QueryParam(value = "idSender") int idSender,
#QueryParam(value = "idReceiver") int idReceiver) {
List<Message> conversion = servicemessage.getConversation(idSender, idReceiver);
return Response.status(Status.OK).entity(conversion).build();
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{idR}")
public Response returnInbox (#QueryParam(value = "idR")int idR){
List<Message> listmessage = servicemessage.returnInbox(idR);
return Response.status(Status.OK).entity(listmessage).build();
}
#DELETE
#Path("{idc}")
public Response deleteConversation(#QueryParam(value = "idc")int id){
servicemessage.deleteConversation(id);
return Response.status(Status.OK).build();
}
}
but whenever I try to consume a service like this one I get this error:
This is the message Entity
package tn.esprit.pi.persistance;
import java.io.Serializable;
import java.lang.String;
import java.util.Date;
import javax.persistence.*;
/**
* Entity implementation class for Entity: Message
*
*/
#Entity(name="message")
public class Message implements Serializable {
/* #EmbeddedId
private MessageId Id;
*/
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String object;
private String content;
#Temporal(TemporalType.TIMESTAMP)
private Date dateEmission;
#ManyToOne
private User sender;
#ManyToOne
private User receiver;
private static final long serialVersionUID = 1L;
public Message(User src, User dest, String content) {
this.sender = src;
this.receiver = dest;
this.content = content;
this.dateEmission = new Date();
}
public Message() {
super();
}
/* public MessageId getId() {
return Id;
}public void setId(MessageId id) {
Id = id;
}
*/
public int getIdMessage() {
return this.id;
}
public String getObject() {
return this.object;
}
public void setObject(String object) {
this.object = object;
}
public User getReceiver() {
return this.receiver;
}
public void setReceiver(User reciver) {
this.receiver = reciver;
}
public User getSender() {
return this.sender;
}
public void setSender(User sender) {
this.sender = sender;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDateEmission() {
return this.dateEmission;
}
public void setDateEmission(Date dateEmission) {
this.dateEmission = dateEmission;
}
}
Double check your URL, I think it should be
http://localhost:8080 not http://localhost:18080. Just a typo mistake.
Secondly, Application base path for restful services set to rest
so url should be
http://localhost:8080/CarMate-web/rest/message?idreceiver=1
Finally, idreceiver is a Query Parameter but #Path("{idreceiver}") treat it as path parameter.
So, In http://localhost:8080/CarMate-web/rest/message?idreceiver=1
Rest Endpoint expect a path variable after /message/{idreceiver}?idreceiver=1 as mentioned by #Leonardo in comments.
I would suggest any of below listed solutions:
Remove #Path from method
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getMessageList(#QueryParam(value = "idreceiver") int idReceiver) {
List<Message> message = servicemessage.getMessageList(idReceiver);
return Response.status(Status.OK).entity(message).build();
}
and use this URL: http://localhost:8080/CarMate-web/rest/message?idreceiver=1
Or Change #Path("{idreceiver}") to #Path("idreceiver")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("idreceiver")
public Response getMessageList(#QueryParam(value = "idreceiver") int idReceiver) {
List<Message> message = servicemessage.getMessageList(idReceiver);
return Response.status(Status.OK).entity(message).build();
}
and use this URL: http://localhost:8080/CarMate-web/rest/message/idreceiver?idreceiver=1

Spring boot integration with Postgres jsonb

I am writing an application which is based on spring boot and tries to store and retrieve data from PostGreSQL db in its jsonb column.
while saving the record it works fine but the moment i write basic method to find records in repository interface of it like this:-
public interface AgentProfileRepository extends CrudRepository<AgentProfileOuter,String> {
public AgentProfileOuter findByJdataPcpAgentId(String id);
}
then server starts giving this exception while restarting:-
Caused by: java.lang.IllegalStateException: Illegal attempt to dereference path source [null.jdata] of basic type
at org.hibernate.jpa.criteria.path.AbstractPathImpl.illegalDereference(AbstractPathImpl.java:98) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.jpa.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:191) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:524) ~[spring-data-jpa-1.9.4.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:478) ~[spring-data-jpa-1.9.4.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:300) ~[spring-data-jpa-1.9.4.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:243) ~[spring-data-jpa-1.9.4.RELEASE.jar:na]
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:148) ~[spring-data-jpa-1.9.4.RELEASE.jar:na]
The point to wonder is when I try to find by id which is a normal numeric column in postgres it works fine but not if i try to find by a key inside json.This thing works successfully with MongoDB.
Here are the bean classes written:-
AgentProfileOuter.java
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Table(name = "Agentbonds")
#Entity
public class AgentProfileOuter {
#GeneratedValue(strategy=GenerationType.AUTO)
#Id
private long id;
#Convert(converter = ConverterAgent.class)
#Column(name="jdata")
private AgentProfile jdata;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public AgentProfile getJdata() {
return jdata;
}
public void setJdata(AgentProfile jdata) {
this.jdata = jdata;
}
}
AgentProfile.java
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Table;
import org.springframework.data.annotation.Id;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"pcpAgentId",
"name",
"bio",
"phone",
"email",
"sms",
"imageUrl"
})
public class AgentProfile {
#JsonProperty("pcpAgentId")
private String pcpAgentId;
/*
public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}
*/
#JsonProperty("name")
private String name;
#JsonProperty("bio")
private String bio;
#JsonProperty("phone")
private String phone;
#JsonProperty("email")
private String email;
#JsonProperty("sms")
private String sms;
#JsonProperty("imageUrl")
private String imageUrl;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
/**
*
* #return
* The pcpAgentId
*/
#JsonProperty("pcpAgentId")
public String getpcpAgentId() {
return pcpAgentId;
}
/**
*
* #param pcpAgentId
* The pcpAgentId
*/
#JsonProperty("pcpAgentId")
public void setAgentId(String pcpAgentId) {
this.pcpAgentId = pcpAgentId;
}
/**
*
* #return
* The name
*/
#JsonProperty("name")
public String getName() {
return name;
}
/**
*
* #param name
* The name
*/
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
/**
*
* #return
* The bio
*/
#JsonProperty("bio")
public String getBio() {
return bio;
}
/**
*
* #param bio
* The bio
*/
#JsonProperty("bio")
public void setBio(String bio) {
this.bio = bio;
}
/**
*
* #return
* The phone
*/
#JsonProperty("phone")
public String getPhone() {
return phone;
}
/**
*
* #param phone
* The phone
*/
#JsonProperty("phone")
public void setPhone(String phone) {
this.phone = phone;
}
/**
*
* #return
* The email
*/
#JsonProperty("email")
public String getEmail() {
return email;
}
/**
*
* #param email
* The email
*/
#JsonProperty("email")
public void setEmail(String email) {
this.email = email;
}
/**
*
* #return
* The sms
*/
#JsonProperty("sms")
public String getSms() {
return sms;
}
/**
*
* #param sms
* The sms
*/
#JsonProperty("sms")
public void setSms(String sms) {
this.sms = sms;
}
/**
*
* #return
* The imageUrl
*/
#JsonProperty("imageUrl")
public String getImageUrl() {
return imageUrl;
}
/**
*
* #param imageUrl
* The imageUrl
*/
#JsonProperty("imageUrl")
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Any help on this is greatly appreciated.
I think, it's because how both Mongo and PostGreSQL structure the data. From Mongo's point, AgentProfileOuter is one document saved in JSON format as key:value. Every field in your AgentProfile class is key for Mongo irrespective of the fact it's another/ child object. However, for PostGreSQL whole AgentProfile object is just one column of String blob, since you have not marked this class as #Entity and it does not have a primary id. So, when you try to search something like pcpAgentId=someid, it does not make any sense to PostGreSQL. This is my guess, verify by checking your data structure in PostGreSQL.
Also noticed that CrudRepository<AgentProfileOuter,String> should be like CrudRepository<AgentProfileOuter,long> since AgentProfilOuter class's primary key is long.

how to make #id fields editable in forge crud?

I want to generate a crud for an entity with an editable primary key but forge doesnt generate the #id field this is my entity note that the id field is an string it is not a an auto increment id field.
package com.samples.model;
// Generated 14/01/2013 11:23:23 AM by Hibernate Tools 3.4.0.CR1
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* Pais generated by hbm2java
*/
#Entity
#Table(name = "pais", schema = "public")
public class Pais implements java.io.Serializable
{
/**
*
*/
private static final long serialVersionUID = -8369022633869576848L;
private String paisId;
private Date fechaRegistro;
private String descripcion;
private short estatus;
private Set<Estado> estados = new HashSet<Estado>(0);
public Pais()
{
}
public Pais(String paisId, Date fechaRegistro, short estatus)
{
this.paisId = paisId;
this.fechaRegistro = fechaRegistro;
this.estatus = estatus;
}
public Pais(String paisId, Date fechaRegistro, String descripcion, short estatus, Set<Estado> estados)
{
this.paisId = paisId;
this.fechaRegistro = fechaRegistro;
this.descripcion = descripcion;
this.estatus = estatus;
this.estados = estados;
}
#Id
#Column(name = "pais_id", unique = true, nullable = false, length = 5)
public String getPaisId()
{
return this.paisId;
}
public void setPaisId(String paisId)
{
this.paisId = paisId;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "fecha_registro", nullable = false, length = 29)
public Date getFechaRegistro()
{
return this.fechaRegistro;
}
public void setFechaRegistro(Date fechaRegistro)
{
this.fechaRegistro = fechaRegistro;
}
#Column(name = "descripcion", length = 30)
public String getDescripcion()
{
return this.descripcion;
}
public void setDescripcion(String descripcion)
{
this.descripcion = descripcion;
}
#Column(name = "estatus", nullable = false)
public short getEstatus()
{
return this.estatus;
}
public void setEstatus(short estatus)
{
this.estatus = estatus;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pais")
public Set<Estado> getEstados()
{
return this.estados;
}
public void setEstados(Set<Estado> estados)
{
this.estados = estados;
}
}
JBoss Forge uses Metawidget for its UI generation. So to accomplish this you must modify Forge's metawidget.xml configuration file.
Forge actually has 3 Metawidget configuration files: metawidget-entity.xml for viewing/editing screens, metawidget-search.xml for search filters, and metawidget-qbe.xml for generating Java code.
For your purposes, you need to modify metawidget-entity.xml. Specifically you must find the JpaInspector entry...
<jpaInspector xmlns="java:org.metawidget.inspector.jpa" config="JpaInspectorConfig">
...and add a child node...
<hideIds><boolean>false</boolean></hideIds>
That bit should be straightforward. However actually getting your modified metawidget-entity.xml back into Forge is clumsy. At the moment the easiest way is probably to unzip and modify the forge-scaffold-faces.jar itself. Hopefully this will be made cleaner in a future Forge release.