Object: entity.ENTITY[ id=null ] is not a known entity type - jpa

this is a similar post to one I have seen before regarding this exception but I am utterly lost. I have yet to persist an entity to a database using JPA, although I have read from tables using it no problem. My setup is Netbeans 7.1 using Glassfish 3.1.1, EclipseLink is my persistence provider. I have a very simple scenario where I just want to test writing a persons name and age into the database and having the id auto increment. Its an MySql database with the fields: Id, FirstName and Age. Heres my code:
Web servlet to take in name and age from html form:
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userPath = request.getServletPath();
if(userPath.equals("/addUser")){
//get request parameters from form
String name = request.getParameter("name");
String age = request.getParameter("age");
//set request attributes to be used by forwarded page
request.setAttribute("name", name);
request.setAttribute("age", age);
//create manager class to add person to database
Manager manager = new Manager();
manager.addPerson(name, age);
userPath = "/result";
}
// use RequestDispatcher to forward request internally
String url = "/WEB-INF/view" + userPath + ".jsp";
try {
request.getRequestDispatcher(url).forward(request, response);
} catch (Exception ex) {
ex.printStackTrace();
}
}
Manager class that takes in name and age, creates a person object and persists it.
public class Manager {
private static final String PERSISTENCE_UNIT_NAME = "FormPU";
private static EntityManagerFactory factory;
public Manager() {
}
public void addPerson(String name, String age) {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
Persons persons = new Persons();
persons.setName(name);
persons.setAge(age);
em.getTransaction().begin();
em.persist(persons);
em.getTransaction().commit();
em.close();
}
}
Persons entity class:
/**
*
* #author esmiala
*/
#Entity
#Table(name = "persons")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Persons.findAll", query = "SELECT p FROM Persons p"),
#NamedQuery(name = "Persons.findById", query = "SELECT p FROM Persons p WHERE
p.id = :id"),
#NamedQuery(name = "Persons.findByFirstName", query = "SELECT p FROM Persons p
WHERE p.firstName = :firstName"),
#NamedQuery(name = "Persons.findByAge", query = "SELECT p FROM Persons p WHERE
p.age = :age")})
public class Persons implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#NotNull
#Column(name = "Id")
private Integer id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "FirstName")
private String firstName;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "Age")
private String age;
public Persons() {
}
public Persons(Integer id) {
this.id = id;
}
public Persons(Integer id, String firstName, String age) {
this.id = id;
this.firstName = firstName;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
#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 Persons)) {
return false;
}
Persons other = (Persons) 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 "entity.Persons[ id=" + id + " ]";
}
}
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="FormPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/form</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>
Note: I have also tried setting exclude-unlisted-classes tag to true and list the class seperately but that didn't work either.
The exception:
WARNING: StandardWrapperValve[Controller]: PWC1406: Servlet.service() for servlet
Controller threw exception
java.lang.IllegalArgumentException: Object: entity.persons[ id=null ] is not a
known entity type.
atorg.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObject
ForPersist(UnitOfWorkImpl.java:4141)
atorg.eclipse.persistence.internal.jpa.EntityManagerImpl.
persist(EntityManagerImpl.java:368)
at manager.Manager.addPerson(Manager.java:36)
at controller.Controller.doPost(Controller.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
...and so on. Any help would be greatly appreciated!

<exclude-unlisted-classes> doesn't work as you would expect - the very presence of this element in persistence.xml disables automatic discovery of #Entity classes, no matter what's inside it.
Also, #Entity(name="persons") is probably not what you want, use #Entity #Table (name="persons") instead.

So you say you can read the class fine, but get an error persisting a new instance?
Can you update an object that you read?
It seems you are having some kind of class loader issue. Somehow you have the class on your classpath twice, or have two different class loaders. The object you are passing to persist is from a different class loader than the one JPA is using. You can check the class loader of what was read, and of the object being persisted to see how they differ.
Have you redeployed you app, or hotdeployed? Does it work if you shut down/restart the server properly. Ensure you are closing your old EntityManagerFactory before redeploying.

Concerning youe concrete problem, try to see if this link helps.
Anyway, the way you are instantiating the EntityManager is not thread safe.
You can see here why. Or, better, you can use NetBeans' wizard for creating JPA controller classes from entity classes, and see how it injects the EntityManager:
#PersistenceContext
private EntityManager em;
See also that the controller classes (the equivalent of your Manager POJO) have the Stateless annotation. This is because you can safely inject an EJB (in this case the EntityManager) only in an object whose lifecycle is managed by the web container (see here for further reference about Accessing Enterprise Beans).

Related

Spring Data JPA #OneToOne mapping is not projected

This question is already phrased as an issue here: https://github.com/spring-projects/spring-data-jpa/issues/2369 but for lack of a reaction there I am copying the contents of that issue here, hoping that somebody might find what's wrong with my code or confirm that this could be a bug:
I've set up an example project here that showcases what seems to be a bug in Spring Data projections: https://github.com/joheb-mohemian/gs-accessing-data-jpa/tree/primary-key-join-column-projection-bug/complete
I have a Customer entity that has a OneToOne mapping to an Address entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToOne(mappedBy = "customer", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
//...
}
#Entity
public class Address {
#Id
#Column(name = "customer_id")
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "customer_id")
private Customer customer;
private String street;
//...
}
Then there are simple projection interfaces:
public interface CustomerProjection {
String getFirstName();
String getLastName();
AddressProjection getAddress();
}
public interface AddressProjection {
String getStreet();
}
But when I try to fetch a projected entity from a repository method like this one:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
//...
<T> T findById(long id, Class<T> type);
}
, getAddress() on the projection will be null, whereas getAddress() when fetching the entity type is populated correctly. Of these two unit tests, only testEntityWithOneToOne()will be successful:
#BeforeEach
void setUpData() {
customer = new Customer("first", "last");
Address address = new Address(customer, "street");
customer.setAddress(address);
entityManager.persist(address);
entityManager.persist(customer);
}
#Test
void testEntityWithOneToOne() {
Customer customerEntity = customers.findById(customer.getId().longValue());
assertThat(customerEntity.getAddress()).isNotNull();
}
#Test
void testProjectionWithOneToOne() {
CustomerProjection customerProjection = customers.findById(customer.getId(), CustomerProjection.class);
assertThat(customerProjection.getAddress()).isNotNull();
}
What's the problem here?

Exception when selecting specific columns using Hibernate and Spring Data JPA

I have a table that has a bytea column (named 'pdf') and I don't want to always select it, specially when I'm returning a list from the database, due to performance issues.
I use native queries with spring data inside the repository to solve these types of situations before (when I used eclipselink), but with Hibernate, if I don't write all the columns in the query, it throws an exception.
For test purposes, I'm trying to select only the id from the User and I still get the exception.
Example: "SELET user.id FROM user WHERE user.id = '1'"
It throws an exception saying that it did not find name in the ResultSet, if I put name in the SQL, it then says age was not found and so on, until I have to write all the columns in the SQL.
Thanks in advance for any help.
What I have tried already:
Updating/Downgrading Hibernate and Spring Data with no luck.
Creating a new entity with only the columns I need, works, but it's a messy solution for me.
Maybe the problem is the combination of the frameworks I use and the way I use them, if someone wants, I could try to upload my whole project structure.
My code:
Entity
#Entity
#Table(name = "user", schema = "portal")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "pdf")
private byte[] pdf;
#Column(name = "name")
private String name;
#Column(name = "age")
private Integer age;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public byte[] getPdf() {
return pdf;
}
public void setPdf(byte[] pdf) {
this.pdf = pdf;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#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 Anexo)) {
return false;
}
Anexo other = (Anexo) 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 "br.gov.to.secad.portal.domain.User[ id=" + id + " ]";
}
}
Service
#Service
#Transactional(readOnly = true)
public class UserService implements Serializable {
private static final long serialVersionUID = 1L;
#Autowired
private IUserRepository userRepository;
public UserService() {
}
public User findOne() {
return userRepository.findOneSQL();
}
}
Repository
public interface IUserRepository extends JpaRepository<User, Serializable>, JpaSpecificationExecutor {
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
public User findOneSQL();
}
The exception:
org.postgresql.util.PSQLException: The column name name was not found in this ResultSet.
Solution
The solution is using an array of Object when I want to select anything less than what I've mapped on my Entity class, thats the limitation of Hibernate that I now understand.
So basically, the method will return Object[] and then I can iterate each position and instantiate a new entity of User with these values.
Example:
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
public Object[] findOneSQL();
I have faced the same problem, I know it is late but well there is a solution that I found elegant.
By the Spring documentation you can declare an interface and from here take the fields you want, in my case it has been something similar to this.
The interface to minimize the fields:
public interface CountryMinify {
String getName();
String getNameTranslation();
}
And my JpaRepository
public interface PlanetRepository extends JpaRepository<Planet, Long> {
#Query(value = "select p.name_country as name, p.name_country_translation as nameTranslation from vm_planet p where gid = ?1", nativeQuery = true)
CountryMinify findByCode(String codeCountry);
}
Keep in mind that the columns should be called the same as gos getter. For example: column name_country -> AS name and the getter of the interface is getName()
Try this
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
Integer findOneSQL();
Call the method like so
Integer user = userRepository.findOneSQL();
Edit 1 :
Since you are using native query you wont be able to use Projections which is a great way of accessing only certain entity fields. There is a JIRA ticket which is still under investigation.
Solution
Return List from your repository like so
#Query(value = "SELECT user.id, user.name FROM user WHERE user.id = '1'", nativeQuery = true)
List<Object[]> findOneSQL();
Iterate over the list of Objects and get your specific columns.
List<Object[]> userNative = userRepository.findOneSQL();
for (Object[] obj : userNative) {
System.out.println("User id : " + obj[0]);
System.out.println("User Name : " + obj[1]);
}

Return more data than model contains using Spring Data

I'm working with Spring Data which is great stuff, but sometimes I need to get more data from database than my model can handle. For example I have model like below.
#Entity
#Table(name = "email")
public class Mail implements Serializable {
#Getter
#Setter
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#Getter
#Setter
private String text;
}
An I my query will be more complex than usual. I want to get my model and in addition number of similar entities, using group by.
#Query(value = "SELECT m, COUNT(m) as countValue FROM Mail m GROUP BY m.text")
List<Mail> findAllNewsletters();
How I should handle something like that? My model does't contain countValue so I will get List<Object[]>
How to deal with that situation, keep my code clean, easiness
of using this.
Step 1: Create a container class to hold the output from your query.
class MailOccurence {
private final Mail mail;
private final Long recurrence;
public MailOccurence(final Mail mail, final Long recurrence) {
this.mail = mail;
this.recurrence = recurrence;
}
public Mail getMail() { return mail; }
public Long getRecurrence() { return recurrence; }
}
Step 2: Populate and return instances of the container class from the query.
Query(value = "SELECT new MailOccurence(m, COUNT(m)) FROM Mail m GROUP BY m.text")
List<MailGroup> findAllNewsletters();
For full details, see the JPA specification.
You can go for a DTO like following
public class MailEntry {
private Long id;
private String text;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
and inside your business logic you can take the advantage of spring template something like following
#Autowired
JdbcTemplate jdbcTemplate;
private static final String SQL = "SELECT m, COUNT(m) as countValue FROM Mail m GROUP BY m.text";
public List<MailEntry> getMailEntries() {
List<MailEntry> mailEntryList = jdbcTemplate.query(SQL, new RowMapper<MailEntry>() {
public MailEntry mapRow(ResultSet rs, int rowNum) throws SQLException {
MailEntry mailEntry = new MailEntry();
mailEntry.setId(rs.getInt(1));
mailEntry.setText(rs.getString(2));
return mailEntry;
}
});
return mailEntryList;
}
Hope this help.

#OneToMany Entity Relationship not filling the list in the entity

I have asked a similar question these days, and after many many hours of trials and fails i find myself compelled to describe my problem from a different point of view.
So I have two entities, WaiterEntity and OrderEntity - as logic demands it, a waiter can have many orders, but an order just one waiter. When the method that persists orders is called, the given order is persisted correctly into the database. But when the waiter is asked about his orders with getOrders(), an empty list is returned. I tried to solve this like many tutorials tell, by (right after persisting the order) getting the list of orders from the waiter and adding the order to the list. Unfortunately there is very strange behaviour to observe: adding the line waiter.getOrders().add(order) somehow prevents or reverts the order to be persisted into the database. But when I try to get the waiter's orders, the all orders that previously were tried to persist appear correctly in the database, but at once the tables of WaiterEntity and OrderEntity become unreadable for JPA. (Although, I can still see the correct table contents through manually called SQL queries.) The only thing that helps is rebuilding the tables.
So maybe some properties of persistence.xml are wrong? The entity annotations are not correctly set up? Or the java code is invalid as I can't tell because I don't have much experience with JPA and GlassFish?
Here are the Entities:
#Entity
#XmlRootElement
#Table(name="WAITERENTITY")
public class WaiterEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.EAGER)
#JoinColumn(name = "waiter_id")
private List<OrderEntity> orders = new ArrayList<>();
... getters and setters
}
#Entity
#XmlRootElement
#Table(name="ORDERENTITY")
public class OrderEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long orderNumber;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "table_id")
private TableEntity table_;
private int sumOfMoney = 0;
private boolean finalized = false;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.LAZY)
#JoinColumn(name = "order_id")
private List<OrderItemEntity> orderItems = new ArrayList<>();
#ManyToOne (cascade = CascadeType.DETACH)
#JoinColumn (name = "waiter_id")
private WaiterEntity waiter;
... getters and setters
}
The method to create orders:
public void create(OrderEntity e) {
WaiterEntity waiter = null;
if (e.getWaiter() != null) {
waiter = em.find(WaiterEntity.class, e.getWaiter().getId());
if (waiter != null) e.setWaiter(waiter);
}
if (e.getTable() != null) {
TableEntity table = em.find(TableEntity.class, e.getTable().getId());
if (table != null) e.setTable(table);
}
em.persist(e);
if (waiter != null) {
waiter = em.find(WaiterEntity.class, e.getWaiter().getId());
//waiter.getOrders().add(e);
}
}
As mentioned, the commented line only makes problems. Further on, without this line, everything in the database is as it should be, as in the foreign keys are set up right, but an obtained WaiterEntity has an empty list, regardless that in fact it has orders in its relationship in the database.
edit: The method that gets a waiter's orders:
public List<OrderEntity> findOrdersByWaiter(#QueryParam("id") long id) {
WaiterEntity waiter = em.find(WaiterEntity.class, id);
return waiter.getOrders();
}
As mentioned, in the situation when the commented line above is un-commented, the strage behaviour starts when calling findOrdersByWaiter(waiter.getId()).
Furthermore, no exceptions are thrown by GlassFish. It's like it just does nothing anymore when calling persitence methods that work with the tables of WaiterEntity and/or OrderEntity.
It would really help if someone more experienced told me what I am doing wrong. If further explanation or code snippets are needed for a better understanding of the situation, I will paste it here. Thanks in advance!
edit 2: (#DonovanMuller) First, a little explanation why there are different object types: I use web resources. The client program and server program communicate using JSON. Only the server knows entity models and persists them. The client does the following (I am not posting all it's methods, just the relevant ones):
IWaiterWebAppClient client = new IWaiterWebAppClient();
client.create(new WaiterBean("John Marston"));
client.create(new WaiterBean("Abigail Marston"));
WaiterBean waiter = client.findByNameSingle(WaiterBean.class, "John Marston");
int rnd = (int) Math.round(Math.random() * 100);
client.create(new TableBean(rnd));
TableBean table = client.findByNameSingle(TableBean.class, String.valueOf(rnd));
OrderBean order = new OrderBean(waiter);
order.setWaiter(null);
client.create(order);
client.create(new OrderBean(waiter, table));
System.out.println(waiter.getName() + "'s OrderBeans:\n" + client.findOrdersByWaiter(waiter.getId()));
while client is an instance of:
public class IWaiterWebAppClient {
private final WebTarget webTarget;
private final Client client;
private static final String BASE_URI = "http://localhost:8080/iWaiter_WebAppServer/webresources";
public IWaiterWebAppClient() {
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI).path("iwaiter");
}
public void close() {
client.close();
}
public <T> void create(T bean) {
webTarget.path(getResourcePath(bean.getClass()))
.request()
.post(Entity.entity(bean,MediaType.APPLICATION_JSON + ";charset=UTF-8"));
}
public <T> T findByNameSingle(Class<T> type, String name) {
List<T> list = findByName(type, name);
return (!list.isEmpty() ? list.get(0) : null);
}
public <T> List<T> findByName(Class<T> type, String name) {
return webTarget.path(getResourcePath(type) + "/findbyname/{name}")
.queryParam("name", name).request(MediaType.APPLICATION_JSON)
.get(constructGenericTypeArrayList(type));
}
public List<OrderBean> findOrdersByWaiter(long id) {
List<OrderBean> list = webTarget.path("order/findbywaiter/{id}")
.queryParam("id", id).request(MediaType.APPLICATION_JSON)
.get(new GenericType<ArrayList<OrderBean>>() {});
return list;
}
private String getResourcePath(Class c) {
if (c.equals(EmployeeView.class)) return "employee";
if (c.equals(WaiterBean.class)) return "waiter";
if (c.equals(TableBean.class)) return "table";
if (c.equals(ItemBean.class)) return "availableitem";
if (c.equals(OrderBean.class)) return "order";
return "";
}
...
}
The fields, getters and setters of WaiterBean and WaiterEntity, as well as OrderBean and OrderEntity, are the same. The only difference is that the 'beans' don't have JPA annotations.
edit 3: (#DonovanMuller) The server is a resource class which methods represent HTTP methods (GET, POST, PUT, DELETE) and exchange information with JSON. The methods of the server have resource annotations, such as:
#GET #Path("order/findbywaiter/{id}")
#Produces("application/json")
public List<OrderEntity> findOrdersByWaiter(#QueryParam("id") long id) { ... }
edit 4: (#DonovanMuller) This is the main part of the web resource class that is responsible for persistence:
#Path("iwaiter")
#Stateless
public class IWaiterResource {
#PersistenceContext(unitName = "ZZZ_WebServicePU")
private EntityManager em;
#Context
private UriInfo context;
#POST #Path(PATH_WAITER)
#Consumes({"application/json", "application/xml"})
public void create(WaiterEntity e) { em.persist(e); }
#POST #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void create(OrderEntity e) {
WaiterEntity waiter = e.getWaiter();
em.persist(e);
if (waiter != null) {
waiter.getOrders().add(e);
}
}
#PUT #Path(PATH_WAITER)
#Consumes({"application/json", "application/xml"})
public void update(WaiterEntity e) { em.merge(e); }
#PUT #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void update(OrderEntity e) { em.merge(e); }
#DELETE #Path(PATH_WAITER)
#Consumes({"application/json", "application/xml"})
public void deleteWaiter(#QueryParam("id") long id) { em.remove(em.find(WaiterEntity.class, id)); }
#DELETE #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void deleteOrder(#QueryParam("id") long id) { em.remove(em.find(OrderEntity.class, id)); }
private <T> List<T> findAll(Class<T> type) {
CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
cq.select(cq.from(type));
return em.createQuery(cq).getResultList();
}
#GET #Path(PATH_WAITER)
#Produces({"application/json", "application/xml"})
public List<WaiterEntity> findAllWaiters() { return findAll(WaiterEntity.class); }
#GET #Path(PATH_ORDER)
#Produces({"application/json", "application/xml"})
public List<OrderEntity> findAllOrders() { return findAll(OrderEntity.class); }
#GET #Path(PATH_WAITER + "/" + PATH_FIND_BY_ID + "/{id}")
#Produces("application/json")
public WaiterEntity findWaiter(#PathParam("id") long id) { return em.find(WaiterEntity.class, id); }
#GET #Path(PATH_ORDER + "/" + PATH_FIND_BY_ID + "/{id}")
#Produces("application/json")
public OrderEntity findOrder(#PathParam("id") long id) { return em.find(OrderEntity.class, id); }
private <T> List<T> findByName(Class<T> type, String column, String searchTag) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(type);
Root<T> root = cq.from(type);
cq.where(cb.equal(root.get(column), searchTag));
return em.createQuery(cq).getResultList();
}
#GET #Path(PATH_WAITER + "/" + PATH_FIND_BY_NAME + "/{name}")
#Produces("application/json")
public List<WaiterEntity> findWaitersByName(#QueryParam("name") String name) { return findByName(WaiterEntity.class, "name", name); }
#GET #Path(PATH_ORDER + "/" + PATH_FIND_BY_NAME + "/{name}")
#Produces("application/json")
public List<OrderEntity> findOrdersByName(#QueryParam("name") String name) { return findByName(OrderEntity.class, "orderNumber", name); }
#GET #Path("order/findbywaiter/{id}")
#Produces("application/json")
public List<OrderEntity> findOrdersByWaiter(#QueryParam("id") long id) {
WaiterEntity waiter = em.find(WaiterEntity.class, id);
return waiter.getOrders();
}
}
Your association is not mapped correctly. A bidirectional always has an owner side and an inverse side. In a OneToMany association, the inverse side must be the one side.
The inverse side does not specify how the association is mapped. It simply says: go look at the other side of the association to see how this association is mapped. This is done using the mappedBy attribute:
In Waiter:
#OneToMany(mappedBy = "waiter", cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.EAGER)
private List<OrderEntity> orders = new ArrayList<>();
In Order:
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "waiter_id")
private WaiterEntity waiter;
The way you have done it, JPA considers waiter.orders and order.waiter as two independent associations, which, unfortunately, are mapped using the same join column. The two associations thus conflict with each other.
I moved this to an answer, as the comments were getting a bit long winded.
I haven't tested this but in your create(OrderEntity e) resource method (edit 4) your waiter reference is surely detached?
#POST #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void create(OrderEntity e) {
WaiterEntity waiter = e.getWaiter(); // <-- getting a reference to a detached entity. I.e. not managed by the entity manager
em.persist(e);
if (waiter != null) {
waiter.getOrders().add(e);
}
}
If you change it to the following, does it solve your problem?
#POST #Path(PATH_ORDER)
#Consumes({"application/json", "application/xml"})
public void create(OrderEntity e) {
em.persist(e);
Waiter waiter = e.getWaiter(); // <-- The reference to the persisted OrderEntity is now managed
if (waiter != null) {
waiter.getOrders().add(e);
}
}

JPA One-to-Many relationship using a List - OrderBy ignored/not working

I'll try to formulate the question more simple:
#Entity
public class One implements Serializable {
...
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
...
First I populate the List with some Many-Entities and persist the One-Entity. Second I retrieve (em.find) the One-Entity expecting the List in ascending order by Many#name, but it's not ordered by name. The List is ordered by id. Complete code see below if necessary.
Original post some days ago:
I'm using a current Netbeans Glassfish bundle.
Product Version: NetBeans IDE 8.0 (Build 201403101706)
Updates: NetBeans IDE is updated to version NetBeans 8.0 Patch 2
Java: 1.7.0_51; Java HotSpot(TM) 64-Bit Server VM 24.51-b03
Runtime: Java(TM) SE Runtime Environment 1.7.0_51-b13
System: Mac OS X version 10.9.3 running on x86_64; UTF-8; de_DE (nb)
The JPA #OrderBy annotation is completely ignored.
#Entity
public class One implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Many> getMany() {
return many;
}
public void setMany(List<Many> many) {
this.many = many;
}
}
The many Entity
#Entity
public class Many implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String name;
public Many() {
}
public Many(String name) {
this.name = 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;
}
}
The service class (EJB)
#Stateless
public class Service {
#PersistenceContext(unitName = "cwPU")
private EntityManager em;
public One createOne() {
return em.merge(new One());
}
public Many createMany(String name) {
return em.merge(new Many(name));
}
public One add(Long oneId, Long manyId) {
One one = em.find(One.class, oneId);
Many many = em.find(Many.class, manyId);
one.getMany().add(many);
return one;
}
public One find(Long id) {
One one = em.find(One.class, id);
return one;
}
}
The main class
public class Main {
public static void main(String[] args) throws NamingException {
EJBContainer container = EJBContainer.createEJBContainer();
Context ctx = container.getContext();
Service service = (Service) ctx.lookup("java:global/classes/Service");
One one = service.createOne();
Many many = service.createMany("the-first");
service.add(one.getId(), many.getId());
many = service.createMany("a-second");
one = service.add(one.getId(), many.getId());
one = service.find(one.getId());
System.out.println("-------------------------------------------------");
for (Many m : one.getMany()) {
System.out.println(m.getName());
}
container.close();
}
}
The output:
the-first
a-second
No matter what I write to the #OrderBy annotation (name ASC, name DESC, id ASC, id DESC), the output is always the same ascending order by the id.
Any idea what I'm missing?
The #Orderby annotation doesn't actually work that way. According to the javadoc, the annotation "Specifies the ordering of the elements of a collection ...at the point when the collection is retrieved."
So the annotation affects the result of the query (find), but does not dictate the order in the collection you store the result set into.
The solution is calling em.refresh (at the right place) as stated from Chris and WPrecht. I had to do this in a separate EJB method.
This did not work:
public One find(Long id) {
em.refresh(em.find(One.class, id)); // did not work
One one = em.find(One.class, id);
return one;
}
Adding a separate refresh method
public void refresh(Long id) {
em.refresh(em.find(One.class, id));
}
and calling it in the main program
...
service.refresh(one.getId());
one = service.find(one.getId());
...
works!
Probably I have to do more reading to understand caching.