I have these two entities in a one to many relation:
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Short id;
#Basic(optional = false)
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "categoryId")
private Collection<Product> productCollection;
...
#XmlTransient
public Collection<Product> getProductCollection() {
return productCollection;
}
...
and
public class Product implements Serializable {
...
#JoinColumn(name = "category_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Category categoryId;
...
generated with NetBeans. The problem is that when the method getProductCollection() is called by the ControllerServlet the Collection of Product is null.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userPath = request.getServletPath();
Category selectedCategory;
Collection<Product> categoryProducts;
// if category page is requested
if (userPath.equals("/category")) {
// get categoryId from request
String categoryId = request.getQueryString();
if (categoryId != null) {
// get selected category
selectedCategory = categoryFacade.find(Short.parseShort(categoryId));
// place selected category in request scope
request.setAttribute("selectedCategory", selectedCategory);
// get all products for selected category
categoryProducts = selectedCategory.getProductCollection();
// place category products in request scope
request.setAttribute("categoryProducts", categoryProducts);
}
Notice the null value of productCollection when other fields has been yet initialized
Edit 1: I declared the categoryFacade in the ControllerServlet applying the #EJB annotation
public class ControllerServlet extends HttpServlet {
#EJB
private CategoryFacade categoryFacade;
Edit 2: Here is the persistence.xml document
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 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_1.xsd">
<persistence-unit name="AffableBeanPU" transaction-type="JTA">
<jta-data-source>jdbc/affablebean</jta-data-source>
<properties/>
</persistence-unit>
</persistence>
Edit 3: I'm using TomEE 7.0.2
Try to initialize the Collection empty like:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "categoryId")
private Collection<Product> productCollection = new HashSet<>();
Then you won't have null values even when there are no results in the lazy loaded relationship. If there are loaded values, they will be added to the collection.
Related
I am having an issue where the Domain (child) is not getting the Business (parents) id. I have the business created and the domain but the domain does not have the business_id. Below is what I was doing originally. I then made a test path that I called which gave the same behavior unless I used the setBusiness method of the domain. Is this the expectation? Do I need to loop over the domains that are passed from a client and set the business?
Test
public void test() {
Business b = new Business();
b.setName("Test Test");
Domain d1 = new Domain();
d1.setName("Domain 1");
d1.setBusiness(b);
em.persist(b);
em.flush();
}
Code
#Entity
#Table(name = "Business")
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy="business", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Domain> domains = new ArrayList<>();
//...getters and setters
}
-
#Entity
#Table(name = "Domain")
public class Domain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "business_id", nullable = false)
private Business business;
//...getters and setters
}
I was calling a rest service using the below JSON which then persisted the business.
{
"name": "Business Test ",
"domains": [{"name": "test domain"}]
}
#Path("business")
public class BusinesResouce {
#EJB
BusinessService service;
#Path("create")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void create(Business entity) {
service.persist(entity);
}
//other paths
}
-
#Stateless
public class BusinessService {
private static final String PERSISTENCE_UNIT_NAME = "edc";
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
private EntityManager em;
public void persist(Business entity) {
em.persist(entity);
//Do I really need to loop entity.getDomains and setBusiness?
em.flush();
}
}
I have a RESTful service called AuthenticationEndpoint, which checks the users username/password pairs, creates token, save token to database and returns token to application client.
Code works without errors and exceptions.
UserEJB bean should save token to Token database table and AuthenticationEJB bean should update token field in User database table. But as I see in MySQL Workbench nothing is saved or updated.
UserEJB.getUsers() method work fine and return list of users and it seems that EJB container found persistence-unit ForthDynamicWebProject, but why I can't save and update entities? Thanks for your attention.
AuthenticationEndpoint RESTful service
#ApplicationScoped
#Path("/authentication")
public class AuthenticationEndpoint {
#Inject
AuthenticateEJB authenticateEJB;
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(String jsonString) {
Gson gson = new Gson();
Credentials credentials = gson.fromJson(jsonString, Credentials.class);
String userName = credentials.getUserName();
String password = credentials.getPassword();
try {
User user = authenticateEJB.checkUser(userName, password);
if ((user instanceof User) && user != null) {
return Response.ok().header("token", user.getToken().getToken()).build();
} else {
return Response.status(Response.Status.FORBIDDEN).build();
}
} catch (Exception e) {
return Response.status(Response.Status.FORBIDDEN).build();
}
}
}
Here is the code of EJBs:
AuthenticateEJB
#LocalBean
#Stateless
public class AuthenticateEJB {
#PersistenceContext(unitName = "ForthDynamicWebProject")
EntityManager em;
#Inject
UserEJB userEJB;
public User checkUser(String userName, String password) {
List<User> users = userEJB.getUsers();
for (User user : users) {
if (user.getUsername().equals(userName) && user.getPassword().equals(password)) {
user.setToken(userEJB.updateToken());
em.merge(user);
return user;
}
}
return null;
}
}
UserEJB
#LocalBean
#Stateless
public class UserEJB {
#PersistenceContext(unitName = "ForthDynamicWebProject")
EntityManager em;
#Inject
Token token;
public List<User> getUsers() {
Query query = em.createQuery("from User");
return (List<User>) query.getResultList();
}
public Token updateToken() {
em.persist(token);
return token;
}
}
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 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_1.xsd">
<persistence-unit name="ForthDynamicWebProject" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>jdbc/second_attempt_hibernate</jta-data-source>
<class>converters.ConverterLocalDateTime</class>
<class>entity.DocStatus</class>
<class>entity.DocType</class>
<class>entity.Document</class>
<class>entity.Employee</class>
<class>entity.MailOrder</class>
<class>entity.MailOrderStatus</class>
<class>entity.Token</class>
<class>entity.User</class>
</persistence-unit>
</persistence>
UPDATE:
Token entity
#Entity
#Table(name = "tokens")
#NamedQuery(name = "Token.findAll", query = "SELECT t FROM Token t")
public class Token implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Temporal(TemporalType.TIMESTAMP)
private Date loginTime;
private String token;
// bi-directional one-to-one association to User
#OneToOne(mappedBy = "token")
private User user;
public Token() {
ConverterLocalDateTime converter = new ConverterLocalDateTime();
loginTime = converter.convertToDatabaseColumn(LocalDateTime.now());
token = Double.toString(Math.random());
}
*getters and setters are not showed*
User entity
#Entity
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String password;
private String username;
//bi-directional one-to-one association to Employee
#OneToOne(mappedBy="user")
private Employee employee;
//bi-directional one-to-one association to Token
#OneToOne
#JoinColumn(name="tokID")
private Token token;
public User() {
}
*getters and setters are not showed*
Maybe I am missing something, but if your user ejb is meant to be bound to a single user, this is not the case, as a stateless can be reused for several other clients, and therefore the token will be reused along with the ooled instance. Check if this is the correct behavior.
Second, you cannot inject the tokens JPA entity or at least not with the current code, the variable would always be null. Maybe this is the reason you are not seeing it persisted.
Third, there are other things I would change, but it's another different question.
I have two objects Antrag (application) and Anlage (facility). An application can be made for multiple facilities. The application is persisted directly in the DAO. The facilities are persisted via cascade.
#Entity
#Table(name = "EEG_ANTRAG")
public class Antrag implements Serializable {
private static final long serialVersionUID = -2440344011443487714L;
#Id
#Column(name = "ANT_ID", nullable = false)
#SequenceGenerator(name = "sequenceGeneratorAntrag", sequenceName = "EEG_ANTRAG_SEQ", allocationSize = 1)
#GeneratedValue(generator = "sequenceGeneratorAntrag")
#Getter #Setter private Long id;
#OneToMany(mappedBy = "antrag", cascade = { CascadeType.ALL }, orphanRemoval = true)
#OrderBy("id ASC")
#Getter private List<Anlage> anlageList = new ArrayList<Anlage>();
public Anlage addAnlage(Anlage anlage)
anlageList.add(anlage);
anlage.setApplication(this);
return anlage;
}
/* some more simple attributes; just Strings, boolean, .. */
}
#Entity
#Table(name = "EEG_ANLAGE")
public class Anlage implements Serializable {
private static final long serialVersionUID = -3940344011443487741L;
#Id
#Column(name = "ANL_ID")
#SequenceGenerator(name = "sequenceGeneratorAnlage", sequenceName = "EEG_ANLAGE_SEQ", allocationSize = 1)
#GeneratedValue(generator = "sequenceGeneratorAnlage")
#Getter #Setter private Long id;
#ManyToOne
#JoinColumn(name = "ANL_ANT_ID")
#Getter #Setter private Antrag antrag;
/* some more simple attributes; just Strings, boolean, .. */
}
#Stateless
public class AntragDaoBean implements AntragDaoLocal {
#PersistenceContext(unitName = "ejb-model")
private EntityManager em;
#Override
public void persistAntrag(Antrag antrag) {
em.persist(antrag);
}
}
When an error occurs on inserting the facilities, e.g. some column name is misspelled in the entity, an exception is thrown. The stacktrace indicates, that a rollback was performed. The problem is, that the application is still persisted. Shouldn't the insertion of the application be rolled back as well?
We are using EclipseLink 2.4.1. The EclipseLink debug output states, that all inserts are performed in one single transaction. The database is Oracle 11g.
Is my ecpectation of the transactional behaviour wrong? How do I get the behaviour I want?
/* shortened exemplary stacktrace for rollback */
EvaluationException:
javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101)
EJBTransactionRolledbackException:
org.jboss.as.ejb3.tx.CMTTxInterceptor.handleEndTransactionException(CMTTxInterceptor.java:115)
RollbackException:
com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1177)
DatabaseException:
org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
SQLSyntaxErrorException:
oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
Your expectation is correct: everything should be made in a single transaction, and the insertion of Antrag should be rolled back as well.
I think your persistence-unit is simply not JTA: test in the persistence.xml file that you have something like:
<persistence-unit name="ejb-model" transaction-type="JTA">
<jta-data-source>java:/someNameDB</jta-data-source>
I have some problems with #Embeddable in JAVA JPA.
I have an entity class named "Author":
#Entity
#Table(name = "author")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Author.findAll", query = "SELECT a FROM Author a"),
...})
public class Author implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "aID")
private Integer aID;
#Column(name = "aName")
private String aName;
#Column(name = "aSurname")
private String aSurname;
#Column(name = "aPhone")
private Integer aPhone;
#Embedded
#AttributeOverrides({
#AttributeOverride(name="city",column=#Column(name="Address")),
#AttributeOverride(name="street",column=#Column(table="Address")),
#AttributeOverride(name="number",column=#Column(table="Address"))
}) private Address address;
// set and get methods.
}
Also I have an Embeddable class named "Address":
#Embeddable
#Table(name = "Address")
#XmlRootElement
public class Address implements Serializable
{
private static final long serialVersionUID=1L;
#Column(name="city")
private String city;
#Column(name="street")
private String street;
#Column(name="number")
private int number;
// get and set methods.
}
In my main class I want to insert this values to the database. (I use mySQL) But I am getting an error on this line: em.getTransaction.commit();
public class CreateAuthor extends javax.swing.JFrame {
private static final String PERSISTENCE_UNIT_NAME = "Project";
private static EntityManagerFactory emf;
public void CreateAuthor() {
initComponents();
}
private void ekleButtonActionPerformed(java.awt.event.ActionEvent evt) {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author author = new Author();
author.setAID(3);
author.setAName("Sheldon");
author.setASurname("Smith");
author.setAPhone(768987);
Address adr = new Address();
adr.setCity("Paris");
adr.setStreet("cinar");
adr.setNumber(12);
author.setAddress(adr);
em.persist(author);
em.getTransaction().commit(); /// error occured
em.close();
}
}
On my database side, I have Author table (aID(pk),aName,aSurname,aPhone)
Address Table (city,street,number)
Do you have any idea why an error is occured?
The goal of Embeddable is to have fields of an object (Address) stored in the same table as the entity's table (Author -> author).
If you want to save them in another table, than Address should be an entity on its own, and there should be a OneToOne or ManyToOne association between Author and Address. The mapping, as is, don't make any sense.
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).