I wanted to try a simple test case with a converter. Unfortunately it doesn't work with payara 5. It works fine with Wildfly 20.0.1. Database is H2.
pom.xml
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<groupId>fjp</groupId>
<artifactId>converter</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
>
<persistence-unit name="primary" transaction-type="JTA">
<!--jta-data-source>java:/TestDS</jta-data-source-->
<jta-data-source>jdbc/TestDS</jta-data-source>
<class>fjp.converter.entity.Employee</class>
<class>fjp.converter.entity.converter.StatusConverter</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
DAO :
package fjp.converter.dao;
import java.util.List;
import fjp.converter.entity.Employee;
public interface EmployeeDAO {
public Employee find(long i);
public void create(Employee e);
public void delete(Employee e);
public void delete(long i);
public List<Employee> findByStatus(Employee.Status status);
}
DAOImpl
package fjp.converter.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import fjp.converter.entity.Employee;
#Stateless
public class EmployeeDAOImpl implements EmployeeDAO {
#PersistenceContext
private EntityManager em;
public Employee find(long i) {
return em.find(Employee.class, i);
}
#Override
public void create(Employee e) {
em.persist(e);
}
#Override
public void delete(long i) {
var e = this.find(i);
if(e != null) em.remove(e);
}
#Override
public void delete(Employee e) {
if(e == null) return;
delete(e.getId());
}
#Override
public List<Employee> findByStatus(Employee.Status status) {
return em.createNamedQuery("Employee.findByStatus", Employee.class)
.setParameter("status", status)
.getResultList();
}
}
Entity :
package fjp.converter.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.NamedQuery;
import java.io.Serializable;
#NamedQuery(name="Employee.findByStatus", query="select e from Employee e where e.status=:status")
#Entity
public class Employee implements Serializable{
public enum Status {
SENIOR("SENIOR"),
JUNIOR("JUNIOR");
private String code;
private Status(String s) {
this.code = s;
}
public String getCode() {
return this.code;
}
}
#Id
private long id;
#Convert(converter = fjp.converter.entity.converter.StatusConverter.class)
private Status status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Status getStatus() {
return this.status;
}
public void setStatus(Status s) {
this.status = s;
}
#Override
public String toString() {
return String.format("id=%d, status=%s", id, status == null ? null : status.getCode());
}
}
Converter :
package fjp.converter.entity.converter;
import javax.persistence.Converter;
import javax.persistence.AttributeConverter;
import fjp.converter.entity.Employee.Status;
#Converter
public class StatusConverter implements AttributeConverter<Status, String> {
#Override
public String convertToDatabaseColumn(Status e) {
return e == null ? null : e.getCode();
}
#Override
public Status convertToEntityAttribute(String s) {
if(s == null) return null;
switch(s) {
case "SENIOR": return Status.SENIOR;
case "JUNIOR": return Status.JUNIOR;
default: return null;
}
}
}
Servlet
package fjp.converter.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServlet;
import fjp.converter.dao.EmployeeDAO;
import fjp.converter.entity.Employee;
import fjp.converter.entity.Employee.Status;
import javax.inject.Inject;
#WebServlet("/test")
public class Test extends HttpServlet {
#Inject
private EmployeeDAO dao;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Employee e = new Employee();
long id = 1;
dao.delete(id);
e.setId(id);
e.setStatus(Status.SENIOR);
dao.create(e);
id = 2;
dao.delete(id);
e.setId(id);
e.setStatus(Status.JUNIOR);
dao.create(e);
Status status = Status.SENIOR;
var list = dao.findByStatus(status);
for(var o : list) {
System.out.println(o);
if(o.getStatus() != status) {
System.out.println("ERROR !!!!!");
}
}
status = Status.JUNIOR;
list = dao.findByStatus(status);
for(var o : list) {
System.out.println(o);
if(o.getStatus() != status) {
System.out.println("ERROR !!!!!");
}
}
}
}
First time you ask the servlet you get the error message :
[2021-05-13T19:08:07.512+0200] [Payara 5.2021.3] [PRÉCIS] [] [org.eclipse.persistence.session./file:/home/frederic/payara5/glassfish/domains/domain1/applications/converter-1.0/WEB-INF/classes/_primary.sql] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1620925687512] [levelValue: 500] [[
SELECT ID, STATUS FROM EMPLOYEE WHERE (STATUS = ?)
bind => [SENIOR]]]
[2021-05-13T19:08:07.514+0200] [Payara 5.2021.3] [INFOS] [] [] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1620925687514] [levelValue: 800] [[
id=2, status=JUNIOR]]
[2021-05-13T19:08:07.514+0200] [Payara 5.2021.3] [INFOS] [] [] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1620925687514] [levelValue: 800] [[
ERROR !!!!!]]
If you refresh the page : it blows !
Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.payara-p3): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException: Violation dindex unique ou clé primaire: {0}
Unique index or primary key violation: {0}; SQL statement:
INSERT INTO EMPLOYEE (ID, STATUS) VALUES (?, ?) [23505-197]
The problem is you are editing an object after you call persist on it in ways not allowed by the JPA specification. What is happening here is you first create Employee e and set its ID and status (1, SENIOR), and call persist on this instance.
You then change the id and status values on e (2, JUNIOR) and again call persist on that same instance. Instance E though is already persisted, so this is ignored. When you query for status SENIOR, EclipseLink will query and find a row matching (1, SENIOR), but when it goes to the cache to look to see if it already has the data, it'll find your 'e' instance and so just return that. Your application logs an error because you've change the state of e to JUNIOR.
For proof of what is happening - try listing out what is in the database.
The solution is just to create a second Employee instance to represent the (2,JUNIOR) data.
Some differences in JPA providers are that EclipseLink will maintain 1st and 2nd level caches by default. This interferes with this situation because you are modifying objects in ways not allowed within the JPA spec, and EclipseLink remembers the data for longer than if there wasn't a cache. You are not allowed to modify primary keys within JPA.
It works if I add to persistence.xml :
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
So you can reuse the Employe but beware of second level cache.
Related
I use payara5 (with EclipseLink). It looks like I can't use subclasses with a JPA converter. With wildfly (and Hibernate), it works fine.
The problem comes from this query :
#Override
public List<Employee> findByStatus(Employee.Status status) {
return em.createNamedQuery("Employee.findByStatus", Employee.class)
.setParameter("status", status)
.getResultList();
}
It looks like, if the converter is a subclass, EclipseLink is not able to convert the parameter "status" into a string.
Without the subclass, it works just fine. Is it a bug in EclipseLink ?
persistence.xml :
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
>
<persistence-unit name="primary" transaction-type="JTA">
<!--jta-data-source>java:/TestDS</jta-data-source-->
<jta-data-source>jdbc/TestDS</jta-data-source>
<class>fjp.converter.entity.Employee</class>
<class>fjp.converter.entity.converter.StatusConverter</class>
<class>fjp.converter.entity.converter.StatusConverterSubClass</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
Entity :
package fjp.converter.entity;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
#NamedQuery(name="Employee.findByStatus", query="select e from Employee e where e.status=:status")
#Entity
public class Employee implements Serializable{
private static final long serialVersionUID = 1L;
public enum Status implements HasCode {
SENIOR("SENIOR"),
JUNIOR("JUNIOR");
private String code;
private Status(String s) {
this.code = s;
}
#Override
public String getCode() {
return this.code;
}
private static Map<String, Status> map = Stream.of(values()).collect(Collectors.toMap(Status::getCode, Function.identity()));
public static Status fromString(String code) {
return map.get(code);
}
}
#Id
private long id;
// #Convert(converter = fjp.converter.entity.converter.StatusConverter.class)
#Convert(converter = fjp.converter.entity.converter.StatusConverterSubClass.class)
private Status status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Status getStatus() {
return this.status;
}
public void setStatus(Status s) {
this.status = s;
}
#Override
public String toString() {
return String.format("id=%d, status=%s", id, status == null ? null : status.getCode());
}
#Override
public boolean equals(Object o) {
if(o == this) return true;
if(!(o instanceof Employee)) return false;
Employee e = (Employee) o;
return e.getId() == getId();
}
#Override
public int hashCode() {
return Long.hashCode(getId());
}
}
Interface HasCode :
package fjp.converter.entity;
public interface HasCode {
String getCode();
}
StatusConverter :
package fjp.converter.entity.converter;
import javax.persistence.Converter;
import javax.persistence.AttributeConverter;
import fjp.converter.entity.Employee.Status;
#Converter
public class StatusConverter implements AttributeConverter<Status, String> {
#Override
public String convertToDatabaseColumn(Status e) {
return e == null ? null : e.getCode();
}
#Override
public Status convertToEntityAttribute(String s) {
if(s == null) return null;
switch(s) {
case "SENIOR": return Status.SENIOR;
case "JUNIOR": return Status.JUNIOR;
default: return null;
}
}
}
StatusConverterSubClass :
package fjp.converter.entity.converter;
import javax.persistence.Converter;
import fjp.converter.entity.Employee.Status;
#Converter
public class StatusConverterSubClass extends EnumCodeConverter<Status> {
public StatusConverterSubClass() {
super(Status::fromString);
}
}
Converter base class :
package fjp.converter.entity.converter;
import java.util.function.Function;
import javax.persistence.AttributeConverter;
import fjp.converter.entity.HasCode;
public class EnumCodeConverter<T extends HasCode> implements AttributeConverter<T, String> {
private final Function<String, ? extends T> fromString;
protected EnumCodeConverter(Function<String, ? extends T> fromString) {
this.fromString = fromString;
}
#Override
public String convertToDatabaseColumn(T attribute) {
return attribute == null ? null : attribute.getCode();
}
#Override
public T convertToEntityAttribute(String code) {
if(code == null) return null;
T r = this.fromString.apply(code);
if(r == null) {
throw new IllegalArgumentException(String.format("unknow code: '%s', '%s'", code, this.getClass()));
}
return r;
}
}
dao :
package fjp.converter.dao;
import java.util.List;
import fjp.converter.entity.Employee;
public interface EmployeeDAO {
public List<Employee> findByStatus(Employee.Status status);
}
daoimpl :
package fjp.converter.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.ejb.Local;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import fjp.converter.entity.Employee;
#Local(EmployeeDAO.class)
#Stateless
public class EmployeeDAOImpl implements EmployeeDAO {
#PersistenceContext
private EntityManager em;
#Override
public List<Employee> findByStatus(Employee.Status status) {
return em.createNamedQuery("Employee.findByStatus", Employee.class)
.setParameter("status", status)
.getResultList();
}
}
Test servlet :
package fjp.converter.servlet;
import javax.inject.Inject;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import fjp.converter.dao.EmployeeDAO;
import fjp.converter.entity.Employee.Status;
#WebServlet("/test")
public class Test extends HttpServlet {
private static final long serialVersionUID = 1L;
#Inject
private EmployeeDAO dao;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Status status = Status.SENIOR;
var list = dao.findByStatus(status);
System.out.println("FJP: " + list.size());
}
}
And payara logs :
[2021-11-11T11:42:56.565+0100] [Payara 5.2021.3] [CONFIG] [] [org.eclipse.persistence.default] [tid: _ThreadID=185 _ThreadName=admin-thread-pool::admin-listener(11)] [timeMillis: 1636627376565] [levelValue: 700] [[
The default table generator could not locate or convert a java type (class fjp.converter.entity.Employee$Status) into a database type for database field (EMPLOYEE.STATUS). The generator uses "java.lang.String" as default java type for the field.]]
[2021-11-11T11:43:21.771+0100] [Payara 5.2021.3] [AVERTISSEMENT] [AS-EJB-00056] [javax.enterprise.ejb.container] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1636627401771] [levelValue: 900] [[
A system exception occurred during an invocation on EJB EmployeeDAOImpl, method: public java.util.List fjp.converter.dao.EmployeeDAOImpl.findByStatus(fjp.converter.entity.Employee$Status)]]
[2021-11-11T11:43:21.772+0100] [Payara 5.2021.3] [AVERTISSEMENT] [] [javax.enterprise.ejb.container] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1636627401772] [levelValue: 900] [[
javax.ejb.EJBException: Exception [EclipseLink-3002] (Eclipse Persistence Services - 2.7.7.payara-p3): org.eclipse.persistence.exceptions.ConversionException
Exception Description: The object [SENIOR], of class [class java.lang.String], from mapping [org.eclipse.persistence.mappings.DirectToFieldMapping[status-->EMPLOYEE.STATUS]] with descriptor [RelationalDescriptor(fjp.converter.entity.Employee --> [DatabaseTable(EMPLOYEE)])], could not be converted to [class fjp.converter.entity.Employee$Status].
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:723)
at com.sun.ejb.containers.EJBContainerTransactionManager.completeNewTx(EJBContainerTransactionManager.java:652)
at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx(EJBContainerTransactionManager.java:482)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4592)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2125)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2095)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:220)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:90)
at com.sun.proxy.$Proxy392.findByStatus(Unknown Source)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.jboss.weld.util.reflection.Reflections.invokeAndUnwrap(Reflections.java:410)
at org.jboss.weld.module.ejb.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:134)
at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)
at org.jboss.weld.module.ejb.InjectionPointPropagatingEnterpriseTargetBeanInstance.invoke(InjectionPointPropagatingEnterpriseTargetBeanInstance.java:68)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106)
at fjp.converter.dao.EmployeeDAO$1921730137$Proxy$_$$_Weld$EnterpriseProxy$.findByStatus(Unknown Source)
at fjp.converter.servlet.Test.doGet(Test.java:36)
logs with FINEST level
[2021-11-13T09:19:12.784+0100] [Payara 5.2021.3] [LE PLUS PRÉCIS] [] [org.eclipse.persistence.default] [tid: _ThreadID=173 _ThreadName=admin-thread-pool::admin-listener(6)] [timeMillis: 1636791552784] [levelValue: 300] [[
Missing class details for [fjp/converter/entity/converter/StatusConverterSubClass].]]
[2021-11-13T09:19:12.784+0100] [Payara 5.2021.3] [LE PLUS PRÉCIS] [] [org.eclipse.persistence.default] [tid: _ThreadID=173 _ThreadName=admin-thread-pool::admin-listener(6)] [timeMillis: 1636791552784] [levelValue: 300] [[
Using existing class bytes for [fjp/converter/entity/converter/StatusConverterSubClass].]]
[2021-11-13T09:19:12.785+0100] [Payara 5.2021.3] [LE PLUS PRÉCIS] [] [org.eclipse.persistence.default] [tid: _ThreadID=173 _ThreadName=admin-thread-pool::admin-listener(6)] [timeMillis: 1636791552785] [levelValue: 300] [[
Missing class details for [fjp/converter/entity/converter/EnumCodeConverter].]]
[2021-11-13T09:19:12.785+0100] [Payara 5.2021.3] [LE PLUS PRÉCIS] [] [org.eclipse.persistence.default] [tid: _ThreadID=173 _ThreadName=admin-thread-pool::admin-listener(6)] [timeMillis: 1636791552785] [levelValue: 300] [[
Using existing class bytes for [fjp/converter/entity/converter/EnumCodeConverter].]]
[2021-11-13T09:19:12.790+0100] [Payara 5.2021.3] [INFOS] [] [org.eclipse.persistence.session./file:/home/frederic/payara5/glassfish/domains/domain1/applications/converter-1.0/WEB-INF/classes/_primary] [tid: _ThreadID=173 _ThreadName=admin-thread-pool::admin-listener(6)] [timeMillis: 1636791552790] [levelValue: 800] [[
EclipseLink, version: Eclipse Persistence Services - 2.7.7.payara-p3]]
[2021-11-13T09:19:12.809+0100] [Payara 5.2021.3] [CONFIG] [] [org.eclipse.persistence.default] [tid: _ThreadID=173 _ThreadName=admin-thread-pool::admin-listener(6)] [timeMillis: 1636791552809] [levelValue: 700] [[
The default table generator could not locate or convert a java type (class fjp.converter.entity.Employee$Status) into a database type for database field (EMPLOYEE.STATUS). The generator uses "java.lang.String" as default java type for the field.]]
It's definitely a bug in EclipseLink.
Fortunately, there is a workaround. The AttributeConverter interface must be added to the subclass. It's totally useless as the superclass already implements it.
I am currently studying a postgrad software development course, and the current part of the course involves setting up a Jakarta EE project utilising Persistence and JSF to create a basic application to enable CRUD operations on entities through a web interface.
I am using Netbeans 12.4, JDK 11 LTS, Payara Server 5.2021.5.
I have successfully setup a Maven project and generated all of the MVC components using the built in tools. One of my three entity classes is as follows:
* 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 entities;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
*
* #author ruebenchandler
*/
#Entity
public class Room implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "room")
private List<CurrentBooking> currentBookings;
public Long getId ()
{
return id;
}
public void setId (Long id)
{
this.id = id;
}
#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 Room))
{
return false;
}
Room other = (Room) 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 "entities.Room[ id=" + id + " ]";
}
}
My persistence.xml is as follows (using the sample Java DB database):
<persistence version="2.2" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="prod" transaction-type="JTA">
<class>entities.Room</class>
<class>entities.Guest</class>
<class>entities.CurrentBooking</class>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
<property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/sample"/>
<property name="javax.persistence.jdbc.user" value="app"/>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="javax.persistence.jdbc.password" value="app"/>
</properties>
</persistence-unit>
</persistence>
On running the application, everything works fine, all CRUD operations work as expected, and when shutting down the application and re-launching it, the records have persisted. But I can't see the new tables in DB view:
Screenshot of tables list
As far as I can tell, the tables are being created and stored somewhere, but I am at a loss as to where.
My tutor had suggested checking my #Table annotations, which I did try without success, but from what I see they aren't required as the database will just use some default values.
Many thanks.
I think the Derby DB is pointed to the wrong DB location in Netbeans
I am fairly new to Wildfly and really new to JPA. I get a null exception when I try to call a method from the DAO. I made some changes suggested using the #Stateless and #Inject annotations, but the DAO does not appear to be initializing at all. The object is null when I try to call the findAllClientCompanies method.
Here is the peristence.xml file.
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="miscPU">
<jta-data-source>java:/jdbc/misc</jta-data-source>
<properties>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.transaction.flush_before_completion" value="true"/>
</properties>
</persistence-unit>
<persistence-unit name="autojobsPU">
<jta-data-source>java:/jdbc/autojobs</jta-data-source>
<properties>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.transaction.flush_before_completion" value="true"/>
</properties>
</persistence-unit>
</persistence>
Here is the entity declaration:
#Entity
#Table(name="clientcompany")
public class ClientCompany extends Company implements Serializable {
Here is the dao, the findAllClientCompanies is the specific method that is blowing up with a null exception:
package com.lingosys.jpa;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;
/**
*
* #author mphoenix
*/
#Stateless
public class ClientCompanyDAO implements Serializable {
#PersistenceContext(unitName="miscPU")
private EntityManager em;
public ClientCompanyDAO() {
}
public void create(ClientCompany clientCompany) {
em.persist(clientCompany);
}
public EntityTransaction getTransaction() {
return em.getTransaction();
}
public ClientCompany findClientCompany(int id) {
ClientCompany clientCompany = (ClientCompany) em.find(ClientCompany.class, id);
return clientCompany;
}
public List <ClientCompany> findAllClientCompanies() {
TypedQuery<ClientCompany> query = em.createQuery("select c from ClientCompany c", ClientCompany.class);
return query.getResultList();
}
public void delete(ClientCompany clientCompany) {
em.remove(em.contains(clientCompany) ? clientCompany:em.merge(clientCompany));
}
public int deleteAllClientCompanies() {
Query query = em.createQuery("delete from ClientCompany");
return query.executeUpdate();
}
}
And here is the JSF bean that calls the dao method:
#Inject
private ClientCompanyDAO daoClientCompany;
private boolean noValidEmail = false;
private String fakeEmailUserName = "";
private Connection conn;
private List<ClientCompany> unsortedList;
private String specialInstructions;
private String createdBy;
//XML processing variables
private Document document = null;
private String xmlMsgs = "";
private boolean xmlLoaderDisabled = false;
//prospect handling variables
private boolean prospectDisabled = false;
private static final String YES = "Yes";
//Legal entity based variables
boolean entitySet = false;
boolean lls = false;
boolean clientIDSet = false;
boolean billingInstructionsSet = false;
boolean billingEmailSet = false;
private static final String[] IS_LLS = {"LLS", "Coto/TI", "LLS-UK"};
boolean companyLoggedOn = false;
private boolean processDisabled = true;
private boolean userSpecificEntity = false;
/**
* Constructor initializes Web Service client, Hibernate DAOs, UI lists, and
* client view.
*
*/
public ClientCreatorBean() {
fmrws = new FormerWSOps();
try {
unsortedList = daoClientCompany.findAllClientCompanies();
view = fmrws.getClientCompanyView();
} catch (Exception ex) {
java.util.logging.Logger.getLogger(ClientCreatorBean.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
reset();
OK it looks like this problem had to do with my invoking the DAO in my constructor. I resolved it by putting the code in another method and prefacing that method with the #PostConstructor annotation.
I'm newbie in EJB and jUnit, and I'm trying to do Embedded testing for the simple EJB-project that running by jBoss AS 7.1.1.Final.
I've written this test:
package com.staff.test.logic;
import java.io.File;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.naming.NamingException;
import org.jboss.as.embedded.EmbeddedServerFactory;
import org.jboss.as.embedded.StandaloneServer;
import org.junit.Before;
import org.junit.Test;
import com.staff.main.logic.ProjectBean;
public class ProjectBeanTest {
private StandaloneServer server;
private static EJBContainer ec;
private static Context ctx;
#Before
public void initContainer() throws Exception {
String jbossHomeDir = System.getenv("JBOSS_HOME");
System.setProperty("jboss.home","C:/eclipse/jboss-as-7.1.1.Final");
assert jbossHomeDir != null;
server = EmbeddedServerFactory.create(new File(jbossHomeDir), System.getProperties(), System.getenv(), "org.jboss.logmanager");
server.start();
ctx=server.getContext();
}
// #After
// public static void closeContainer() throws Exception {
// if (ec != null) {
// ec.close();
// }
// }
#Test
public void test() throws NamingException {
ProjectBean bean = (ProjectBean) ctx.lookup("java:global/ProjectBean");
}
}
But string
ProjectBean bean = (ProjectBean) ctx.lookup("java:global/ProjectBean") make exception:
do this exception:
javax.naming.NameNotFoundException: ProjectBean -- service jboss.naming.context.java.global.ProjectBean
at org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBa sedNamingStore.java:97)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java: 178)
at org.jboss.as.naming.InitialContext.lookup(InitialContext.jav a:123)
at org.jboss.as.naming.NamingContext.lookup(NamingContext.java: 214)
at javax.naming.InitialContext.lookup(Unknown Source)
at com.staff.test.logic.ProjectBeanTest.test(ProjectBeanTest.ja va:80)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall( FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(Refl ectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(Fr ameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate( InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(Ru nBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271 )
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit 4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit 4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java: 63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java :236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java: 53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java: 229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.r un(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(Test Execution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTe sts(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTe sts(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(R emoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main( RemoteTestRunner.java:197)
I dont understand why this string do exception, because I'm newbie.
My project have a bean "ProjectBean" with interface "ProjectBeanLocal" and an entity "Post". And I have a file persistence.xml to work with Oracle 10g.
ProjectBean:
package com.staff.main.logic;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import com.staff.main.domain.Post;
/**
* Session Bean implementation class ProjectBean
*/
#Stateless
#LocalBean
public class ProjectBean implements ProjectBeanLocal {
#PersistenceContext(unitName="StaffPU")
EntityManager manager;
public ProjectBean()
{
Ejb3Configuration cfg = new Ejb3Configuration();
cfg.configure("StaffPU", null);
SchemaExport schemaExport = new SchemaExport(cfg.getHibernateConfiguration());
//schemaExport.setOutputFile("schema.sql");
schemaExport.create(true, true);
}
#Override
public void savePost(Post post){
manager.persist(post);
}
#Override
public Post findPost(long id) {
Post post=manager.find(Post.class, id);
return post;
}
}
Interface ProjectBeanLocal:
package com.staff.main.logic;
import javax.ejb.Local;
import com.staff.main.domain.Post;
#Local
public interface ProjectBeanLocal {
void savePost(Post post);
Post findPost(long id);
}
Entity "Post":
package com.staff.main.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
#Entity
public class Post implements Serializable{
private static final long serialVersionUID = 6767319776206583629L;
#Id
#SequenceGenerator(name="ent2seq",sequenceName="seq_post")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="ent2seq")
private long id;
#Column(nullable=false)
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;
}
}
And persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="StaffPU">
<jta-data-source>java:jboss/datasources/OracleDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-- <property name="javax.persistence.jdbc.show_sql" value="true" /> -->
<!-- <property name="hibernate.show_sql" value="true" /> -->
</properties>
</persistence-unit>
</persistence>
You need to expose your bean with a Remote interface.
After that, you probably will need to change the JNDI entry name used to lookup the ejb reference.
You don't provide information about how the bean is deployed which, among other things, determines the binding name, but you can search the correct lookup string in the server log.
import java.io.File;
import javax.naming.InitialContext;
import org.jboss.ejb3.embedded.EJB3StandaloneBootstrap;
import org.jboss.ejb3.embedded.EJB3StandaloneDeployer;
in your pom you have to add :
<dependency>
<groupId>org.jboss.embedded</groupId>
<artifactId>jboss-embedded-all</artifactId>
<version>beta3.SP9</version>
</dependency>
<dependency>
<groupId>org.jboss.embedded</groupId>
<artifactId>thirdparty-all</artifactId>
<version>beta3.SP9</version>
</dependency>
you have to add :
embedded-jboss-beans.xml
bean.xml,default.persistence.properties
ejb3-interceptors-aop.xml
into your resources folder and jndi.properties too whtch contains :
java.naming.factory.initial=org.jnp.interfaces.LocalOnlyContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
and you test class must contains :
EJB3StandaloneBootstrap.boot(null);
EJB3StandaloneBootstrap.scanClasspath();
EJB3StandaloneBootstrap.deployXmlResource(embeded.xml)
deployer = EJB3StandaloneBootstrap.createDeployer();
deployer.getArchives().add(new File("target/classes").toURI().toURL());
deployer.create();
deployer.start();
InitialContext context = new InitialContext();
and you look up your ejb
I have just started learning JAX-RS and am trying to modify some examples from the O'Reilly RESTful Java with JAX-RS book. I've run into an issue where I am getting a null pointer exception when I try and POST an XML file to one of my JAX-RS services. The specific resource I am posting to uses JPA to persist information to a derby database. After reading several other question/responses and tutorials I am convinced my code is correct but perhaps I am missing some configuration. It seems the entity manager isn't being injected for some reason even though I have the appropriate annotations. Any input on my issue would be appreciated. Please see the following excerpts of my project that I think will be useful:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="jpa-example" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>java:comp/env/jdbc/DerbyConnection</jta-data-source>
<class>com.example.persistence.UserEntity</class>
<class>com.example.persistence.SearchEntity</class>
<properties>
<property name="openjpa.TransactionMode" value="managed"/>
<property name="openjpa.ConnectionFactoryMode" value="managed"/>
<property name="openjpa.LockTimeout" value="30000"/>
<property name="openjpa.jdbc.TransactionIsolation" value="read-committed"/>
<property name="openjpa.Log" value="TRACE"/>
<property name="openjpa.jdbc.UpdateManager" value="operation-order"/>
</properties>
</persistence-unit>
</persistence>
server.xml
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>jsp-2.2</feature>
<feature>jdbc-4.0</feature>
<feature>jpa-2.0</feature>
<feature>localConnector-1.0</feature>
<feature>jaxrs-1.1</feature>
<feature>ejbLite-3.1</feature>
</featureManager>
<httpEndpoint host="localhost" httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>
<jdbcDriver id="derbyJDBCDriver">
<library name="DerbyLib">
<fileset dir="/Users/jackson/Documents/db-derby-10.10.1.1-bin/lib" includes="derby.jar"/>
</library>
</jdbcDriver>
<dataSource id="DerbyConnection" jdbcDriverRef="derbyJDBCDriver" jndiName="jdbc/DerbyConnection">
<properties.derby.embedded createDatabase="create" databaseName="example"/>
</dataSource>
<applicationMonitor updateTrigger="mbean"/>
<webApplication id="REST" location="REST.war" name="REST"/>
</server>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>REST</display-name>
<servlet>
<description>
JAX-RS Tools Generated - Do not modify</description>
<servlet-name>JAX-RS Servlet</servlet-name>
<servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.example.services.RESTConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<enabled>true</enabled>
<async-supported>false</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Servlet</servlet-name>
<url-pattern>
/rest/*</url-pattern>
</servlet-mapping>
<ejb-local-ref>
<ejb-ref-name>ejb/UserResource</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>com.example.services.UserResource</local>
<ejb-link>
com.example.services.UserResourceBean
</ejb-link>
</ejb-local-ref>
</web-app>
RESTConfig.java
package com.example.services;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class RESTConfig extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(HelloWorld.class);
classes.add(UserResourceBean.class);
return classes;
}
}
UserEntity.java
package com.example.persistence;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity(name = "User")
public class UserEntity {
private long id;
private String login;
private String password;
private String firstName;
private String lastName;
private String email;
private String role;
private String status;
#Id
#GeneratedValue
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Override
public String toString()
{
return "UserEntity {" +
"id=" + id +
", email='" + email + '\'' +
", password='" + password + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", role='" + role + '\'' +
", status='" + status + '\'' +
'}';
}
}
UserResource.java
package com.example.services;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import com.example.domain.User;
import com.example.domain.Users;
#Path("/users")
public interface UserResource
{
#POST
#Consumes("application/xml")
Response createUser(User user, #Context UriInfo uriInfo);
#GET
#Produces("application/xml")
//#Formatted
Users getUsers(#QueryParam("start") int start,
#QueryParam("size") #DefaultValue("10") int size,
#QueryParam("firstName") String firstName,
#QueryParam("lastName") String lastName,
#Context UriInfo uriInfo);
#GET
#Path("{id}")
#Produces("application/xml")
User getUser(#PathParam("id") long id);
}
UserResourceBean.java
package com.example.services;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import com.example.domain.Link;
import com.example.domain.User;
import com.example.domain.Users;
import com.example.persistence.UserEntity;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
#Stateless
public class UserResourceBean implements UserResource
{
#PersistenceContext(unitName="jpa-example")
private EntityManager em;
public Response createUser(User user, UriInfo uriInfo)
{
UserEntity entity = new UserEntity();
domain2entity(entity, user);
System.out.println(entity);
em.persist(entity);
em.flush();
System.out.println("Created user " + entity.getId());
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.path(Long.toString(entity.getId()));
return Response.created(builder.build()).build();
}
public User getUser(long id)
{
UserEntity user = em.getReference(UserEntity.class, id);
return entity2domain(user);
}
public static void domain2entity(UserEntity entity, User user)
{
entity.setId(user.getId());
entity.setLogin(user.getLogin());
entity.setPassword(user.getPassword());
entity.setFirstName(user.getFirstName());
entity.setLastName(user.getLastName());
entity.setEmail(user.getEmail());
entity.setRole(user.getRole());
entity.setStatus(user.getStatus());
}
public static User entity2domain(UserEntity entity)
{
User u = new User();
u.setId(entity.getId());
u.setLogin(entity.getLogin());
u.setPassword(entity.getPassword());
u.setFirstName(entity.getFirstName());
u.setLastName(entity.getLastName());
u.setEmail(entity.getEmail());
u.setRole(entity.getRole());
u.setStatus(entity.getStatus());
return u;
}
public Users getUsers(int start,
int size,
String firstName,
String lastName,
UriInfo uriInfo)
{
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.queryParam("start", "{start}");
builder.queryParam("size", "{size}");
ArrayList<User> list = new ArrayList<User>();
ArrayList<Link> links = new ArrayList<Link>();
Query query = null;
if (firstName != null && lastName != null)
{
query = em.createQuery("select u from Users u where u.firstName=:first and u.lastName=:last");
query.setParameter("first", firstName);
query.setParameter("last", lastName);
}
else if (lastName != null)
{
query = em.createQuery("select u from Users u where u.lastName=:last");
query.setParameter("last", lastName);
}
else
{
query = em.createQuery("select u from Users u");
}
List userEntities = query.setFirstResult(start)
.setMaxResults(size)
.getResultList();
for (Object obj : userEntities)
{
UserEntity entity = (UserEntity) obj;
list.add(entity2domain(entity));
}
// next link
// If the size returned is equal then assume there is a next
if (userEntities.size() == size)
{
int next = start + size;
URI nextUri = builder.clone().build(next, size);
Link nextLink = new Link("next", nextUri.toString(), "application/xml");
links.add(nextLink);
}
// previous link
if (start > 0)
{
int previous = start - size;
if (previous < 0) previous = 0;
URI previousUri = builder.clone().build(previous, size);
Link previousLink = new Link("previous", previousUri.toString(), "application/xml");
links.add(previousLink);
}
Users users = new Users();
users.setUsers(list);
users.setLinks(links);
return users;
}
}
It is in this last file in which the NPE occurs. Specifically in function createUser, the following code throws a NPE: em.persist(entity);
I solved the injection issue by altering the code in RESTconfig.java to appear as follows:
package com.example.services;
import java.util.HashSet;
import java.util.Set;
import javax.naming.InitialContext;
import javax.ws.rs.core.Application;
public class RESTConfig extends Application {
public Set<Object> getSingletons()
{
HashSet<Object> set = new HashSet();
try
{
InitialContext ctx = new InitialContext();
obj = ctx.lookup(
"java:comp/env/ejb/UserResource");
set.add(obj);
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
return set;
}
}