I'm trying to build an application with Netbean. I use Eclipse IDE & JPA API. the Entities is like this:
NATS.java
#Entity
#Table(name = "NATS")
Public Class NATS implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer code;
private String nName;
#Column(name = "cap_id") // for foreign key purpose
private Integer capId;
#OneToOne(cascade=CascadeType.PERSIST)
#PrimaryKeyJoinColumn(name = "cap_id" )
private City capital;
public City getCapital(){
return this.capital;
}
public void setCapital (City newcapital){
this.capital = newcapital;
}
... some gets & sets methods
}
City.java
#Entity
public class City implements Serializable {
private static final long serialVersionUID = 1L;
private String cityName;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Integer cityId;
public String getCityName() {
return cityName;
}
public void setCityName(String newcityName) {
this.cityName = newcityName;
}
public City(String ctname) {
this.cityName = ctname;
}
public Integer getcityId() {
return cityId;
}
public void setcityId(Integer ctId) {
this.cityId = ctId;
}
}
When i want to add new pair of NATS & City, i use this :
somebean.java
#Stateless
public class somebean {
#PersistenceContext(unitName = "TestLocal")
private EntityManager em;
public NATs insertNewCC(String capitalname, String countryname){
City newcapital = new City(capitalname );
NATS newcountry = new NATS();
newcountry.setNationName(countryname);
newcountry.setCapital(newcapital);
em.persist(newcountry); // both objects persisted well, with "capId" still null
return newcountry;
}
public void updateCapitalId(Nations country){
country.setCapitalId(country.getCapital().getcityId());
em.merge(country);
}
}
Here is the service:
genericResource.java
#Path("generic")
public class GenericResource {
#Context
private UriInfo context;
#EJB
private somebean r;
#GET
#Path("/inscountry")
#Produces("application/json")
public List<NATS> insCountry( #QueryParam("countryname") String countryname, #QueryParam("capitalname") String capitalname){
NATS newcountry = r.insertNewCC(capitalname, countryname);
//r.updateCapitalId(newcountry); <-- i want to avoid using this line
List<NATS> result= r.getListNATS();
return result;
}
When i comment the line : r.updateCapitalId(newcountry);
i get a pair of country and capital city with a relation displayed correctly in JSON, but when the session closes. it losses the relation because the foreign key is not saved. the capId in NATs entity is null after persist completed. So i need 1 persist & 1 merge to complete this. is there a better solution beside using that line i commented?
Thank you.
Redesign your entity NATS like this :
package com;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name = "NATS")
public class NATS
implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer code;
private String name;
// for foreign key purpose
#OneToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "cap_id")
private City capital;
public City getCapital()
{
return this.capital;
}
public void setCapital(City newcapital)
{
this.capital = newcapital;
}
public Integer getCode()
{
return this.code;
}
public void setCode(Integer code)
{
this.code = code;
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
}
and then code for execution will remain same :
City newcapital = new City(capitalname);
NATS newcountry = new NATS();
newcountry.setName(countryname);
newcountry.setCapital(newcapital);
this.entityManager.persist(newcountry);
First of all, try to use indents and a formatter because your code is a mess. Second you want to save two entities (NATS and Country) so you must to uncomment the line where you update the city or persist one by one. If you are starting a new application I recommend you to use Spring boot and it's SpringRepository Interface to make those kind of things easier and clear.
Related
I use the hibernate-JPA implementation (v5.6.1.Final) in my project.
I have implemented the data access layer as follows:
Class Visualization Diagram.
1.1 Employee.java Entity
package com.elephasvacation.tms.web.entity;
import com.elephasvacation.tms.web.entity.enumeration.GenderTypes;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
#Entity
#Table(name = "employee")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Employee implements SuperEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "name", nullable = false, length = 200)
private String name;
#Lob
#Column(name = "address")
private String address;
#Column(name = "date_of_birth")
private LocalDate dateOfBirth;
#Column(name = "nic", length = 45)
private String nic;
#Column(name = "contact", length = 45)
private String contact;
#Column(name = "email", length = 200)
private String email;
#Column(name = "gender", length = 20)
private GenderTypes gender;
#Column(name = "position", length = 45)
private String position;
#Column(name = "status", length = 45)
private String status;
#Column(name = "created")
private LocalDateTime created;
#Column(name = "updated")
private LocalDateTime updated;
/* Constructor with ID attribute. */
public Employee(Integer id,
String name,
String address,
LocalDate dateOfBirth,
String nic,
String contact,
String email,
GenderTypes gender,
String position,
String status) {
this.id = id;
this.name = name;
this.address = address;
this.dateOfBirth = dateOfBirth;
this.nic = nic;
this.contact = contact;
this.email = email;
this.gender = gender;
this.position = position;
this.status = status;
}
/* Constructor without ID attribute. */
public Employee(String name,
String address,
LocalDate dateOfBirth,
String nic,
String contact,
String email,
GenderTypes gender,
String position,
String status) {
this.name = name;
this.address = address;
this.dateOfBirth = dateOfBirth;
this.nic = nic;
this.contact = contact;
this.email = email;
this.gender = gender;
this.position = position;
this.status = status;
}
#PrePersist
public void creationTimeStamps() {
created = LocalDateTime.now();
}
#PreUpdate
public void updateTimeStamps() {
updated = LocalDateTime.now();
}
}
I want to return the Generated ID when an object is persisted successfully. So, I Implemented EmployeeDAOImpl.java as follows:
1.2 EmployeeDAOImpl.java
package com.elephasvacation.tms.web.dal.custom.impl;
import com.elephasvacation.tms.web.dal.custom.EmployeeDAO;
import com.elephasvacation.tms.web.entity.Employee;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;
public class EmployeeDAOImpl implements EmployeeDAO {
private EntityManager entityManager;
#Override
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public Integer save(Employee employee) throws Exception {
this.entityManager.persist(employee);
// call the flush method on EntityManager manually, because we need to get the Generated ID
this.entityManager.flush();
return employee.getId(); // here, generated ID will be returned.
}
#Override
public void update(Employee employee) throws Exception {
this.entityManager.merge(employee);
}
#Override
public void delete(Integer key) throws Exception {
this.entityManager.remove(this.entityManager.find(Employee.class, key));
}
#Override
public Employee get(Integer key) throws Exception {
return this.entityManager.find(Employee.class, key);
}
#Override
public List<Employee> getAll() throws Exception {
Query allEmployeesQuery = this.entityManager.createQuery("SELECT e FROM Employee e");
return (List<Employee>) allEmployeesQuery.getResultList();
}
}
I am refactoring the code as follows:
by creating CrudDAOImpl.java
Class Visualization Diagram After Using CrudDAOImpl.java
2.1 CrudDAOImpl.java
package com.elephasvacation.tms.web.dal;
import com.elephasvacation.tms.web.entity.SuperEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
public class CrudDAOImpl<T extends SuperEntity, K extends Serializable> implements CrudDAO<T, K> {
private EntityManager entityManager;
private Class<T> entityClass;
public CrudDAOImpl() {
this.entityClass =
(Class<T>) (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]);
}
/** This method is used to pass the EntityManager to the lower level classes that extend the CrudDAOImpl class.
* */
protected EntityManager getEntityManager(){
return this.entityManager;
}
#Override
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public Integer save(T entity) throws Exception {
/* If native hibernate is used in my project. I can do something like this;
this will return the generated ID:
* this.session.save(entity);
*
* Since I go with hibernate-JPA implementation I want to do the same thing in this method. */
this.entityManager.persist(entity);
this.entityManager.flush();
return null; // I want to return the generated ID here.
}
#Override
public void update(T entity) throws Exception {
this.entityManager.merge(entity);
}
#Override
public void delete(K key) throws Exception {
this.entityManager.remove(key);
}
#Override
public T get(K key) throws Exception {
return this.entityManager.find(this.entityClass, key);
}
#Override
public List<T> getAll() throws Exception {
TypedQuery<T> query =
this.entityManager.createQuery("SELECT " + (this.entityClass.getName()), this.entityClass);
return query.getResultList();
}
}
I would appreciate it if you could please suggest to me a way to return the generated ID when an object persists in the database. Thanks in advance.
If you are interested here's what I have done. I have used SuperEntity.java as #HasnainAliBohra suggested.
Add a getId() method to SuperEntity.java.
package com.elephasvacation.tms.web.entity;
import java.io.Serializable;
public interface SuperEntity extends Serializable {
<T extends Serializable> T getId();
}
Mostly, generated ID will be a java.lang.Integer, So it is Serializable.
Create a getter for the id attribute in Employee.java. Please note that I have used Project Lombok. So, the #Data annotation generates the getter for the id attribute.
Anyhow, getter method looks like this:
// Employee.java class
public Integer getId() {
return id;
}
Change the save() method in CrudDAOImpl.java as follows:
#Override
public Integer save(T entity) throws Exception {
this.entityManager.persist(entity);
this.entityManager.flush();
return entity.getId(); // Let's return the generated ID here.
}
Testing what I have done (with JUnit4).
package com.elephasvacation.tms.web.dal.custom.impl;
import com.elephasvacation.tms.web.dal.DAOFactory;
import com.elephasvacation.tms.web.dal.DAOTypes;
import com.elephasvacation.tms.web.dal.custom.EmployeeDAO;
import com.elephasvacation.tms.web.entity.Employee;
import com.elephasvacation.tms.web.entity.enumeration.GenderTypes;
import com.elephasvacation.tms.web.util.HibernateUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.time.LocalDate;
import static org.junit.Assert.assertNotNull;
public class EmployeeDAOImplTest {
private final EmployeeDAO employeeDAO = DAOFactory.getInstance().getDAO(DAOTypes.EMPLOYEE);
private EntityManagerFactory emf;
private EntityManager em;
#Before
public void setUp() {
try {
/* get EntityManagerFactory. */
this.emf = HibernateUtil.getEntityManagerFactory();
/* creates EntityManager. */
this.em = emf.createEntityManager();
} catch (Exception e) {
e.printStackTrace();
}
}
#After
public void tearDown() {
/* close the EntityManagerFactory and EntityManager. */
if (em != null) {
em.close();
emf.close();
}
}
#Test
public void save() {
try {
/* begins the transaction. */
this.em.getTransaction().begin();
/* set EntityManager. */
this.employeeDAO.setEntityManager(this.em);
/* creates a new Employee object. */
Employee john = new Employee("John Doe",
"New York",
LocalDate.of(1991, 01, 01),
"112233445566",
"03321234567",
"john.test#gmail.com",
GenderTypes.MALE,
"Trainee",
"Active");
/* saving the Employee. */
Integer generatedEmployeeId = this.employeeDAO.save(john);
/* assert */
assertNotNull(generatedEmployeeId);
/* print the generated ID on the terminal. */
System.out.println("Generated Employee ID: " + generatedEmployeeId);
/* committing the transaction. */
this.em.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Test case did run successfully and the database record persisted in the employee table.
5.1 Terminal output
5.2 Database record screenshot.
Hope you find something useful, thank you.
I have 2 different tables: subjects and questions and I need to make SQL JOIN on these 2 tables. Table subjects has its attributes: name and shortcut. Table questions has its attributes: question_number, text, subject - in fact, subject from table questions is a shortcut of a subject.
I tried something like this, what I saw in one stackoverflow topic:
Query q = em.createNativeQuery("SELECT q.question_number, q.text, s.name, s.shortcut FROM "
+ "( questions q INNER JOIN subjects s ON q.subject=s.shortcut );", QuestionSubject.class);
QuestionSubject.class is an #Entity class and has attributes of both questions table and subjects table. After calling this method I saw that a new table with a name QUESTIONSUBJECT was created in my database and that is what I do not want to be done.
Can anyone help me with other solution?
P.S.: I am doing this in order to use the ouput as a response on HTTP request so I need to gather those two into one. I need to return either a List or JSON string.
EDIT: Using MySQL database.
questions table Entity class:
package model;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "questions")
#XmlRootElement
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "question_number")
private Integer questionNumber;
#Column(name = "text")
private String text;
#Column(name = "subject")
private String subject;
public Question() {
}
public Question(Integer questionNumber) {
this.questionNumber = questionNumber;
}
public Question( String text, String subject) {
this.text = text;
this.subject = subject;
}
public Question(Integer questionNumber, String text, String subject) {
this.questionNumber = questionNumber;
this.text = text;
this.subject = subject;
}
public Integer getQuestionNumber() {
return questionNumber;
}
public void setQuestionNumber(Integer questionNumber) {
this.questionNumber = questionNumber;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
#Override
public int hashCode() {
int hash = 0;
hash += (questionNumber != null ? questionNumber.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 Question)) {
return false;
}
Question other = (Question) object;
if ((this.questionNumber == null && other.questionNumber != null) || (this.questionNumber != null && !this.questionNumber.equals(other.questionNumber))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Rest.Questions[ questionNumber=" + questionNumber + " ]";
}
}
subjects table Entity class.
package model;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "subjects")
#XmlRootElement
public class Subject implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 5)
#Column(name = "shortcut")
private String shortcut;
#Basic(optional = false)
#NotNull
#Lob
#Size(min = 1, max = 65535)
#Column(name = "name")
private String name;
public Subject() {
}
public Subject(String shortcut) {
this.shortcut = shortcut;
}
public Subject(String shortcut, String name) {
this.shortcut = shortcut;
this.name = name;
}
public String getShortcut() {
return shortcut;
}
public void setShortcut(String shortcut) {
this.shortcut = shortcut;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
int hash = 0;
hash += (shortcut != null ? shortcut.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 Subject)) {
return false;
}
Subject other = (Subject) object;
if ((this.shortcut == null && other.shortcut != null) || (this.shortcut != null && !this.shortcut.equals(other.shortcut))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Rest.Subjects[ shortcut=" + shortcut + " ]";
}
}
QuestionSubject Entity class:
package model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class QuestionSubject implements Serializable
{
#Id
#Column(name = "question_number")
private Integer questionNumber;
#Column(name = "text")
private String text;
#Column(name = "shortcut")
private String shortcut;
#Column(name = "name")
private String name;
public Integer getQuestionNumber() {
return questionNumber;
}
public void setQuestionNumber(Integer questionNumber) {
this.questionNumber = questionNumber;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getShortcut() {
return shortcut;
}
public void setShortcut(String shortcut) {
this.shortcut = shortcut;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The table gets created because you define a class named QuestionSubject annotated as #Entity. Per default the table name is the class name.
You could override the name like you did in Subjects with #Table(name = "subjects")
Nearly the same would happen if you would define a #ManyToMany mapping on related fields between classes Question and Subject without defining QuestionSubject class at all.
I would recommend to take a look here to get more information:
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Relationship_Mappings/Collection_Mappings/ManyToMany
Edit
If you need a manyToMany mapping you need this table. Otherwise you can only have an oneToMany resp. manyToOne relation (using foreign keys).
I have two DTO objects say A and B which are having getters and setters and are used to take data from the database. The problem is when I am calling A, B gets called and B again points itself to A and a cycle is created.
I cannot ignore/hide the method which is creating the cycle. I need to take the whole data of A and B.
Is there any way to achieve it ?
Please help
This is my code which is causing the problem. This is application DTO which is calling environment DTO
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
And this is environment DTO which is calling the application DTO
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Here cycle is getting created
This is my rest call which will give result in XML format and I think while creating XML cycle is getting created
#GET
#Path("/get")
#Produces({MediaType.APPLICATION_XML})
public List<ApplicationDTO> getAllApplications(){
List<ApplicationDTO> allApplication = applicationService.getAllApplication();
return allApplication;
}
This is the Application DTO class
#Entity
#Table(name="application")
#org.hibernate.annotations.GenericGenerator(
name ="test-increment-strategy",strategy = "increment")
#XmlRootElement
public class ApplicationDTO implements Serializable {
#XmlAttribute
public Long appTypeId;
private static final long serialVersionUID = -8027722210927935073L;
private Long applicationId;
private String applicationName;
private ApplicationTypeDTO applicationType;
private String applicationDescription;
private Integer owner;
private Integer createdBy;
private Integer assignedTo;
private Date createTime;
private Date modifiedTime;
private Set<EnvironmentDTO> environment;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "applicationId")
public Long getApplicationId() {
return applicationId;
}
private void setApplicationId(Long applicationId) {
this.applicationId = applicationId;
}
#Column(name = "applicationName")
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
#ManyToOne(targetEntity=ApplicationTypeDTO.class
,fetch = FetchType.LAZY
)
#JoinColumn(name="applicationType")
public ApplicationTypeDTO getApplicationType() {
return applicationType;
}
public void setApplicationType(ApplicationTypeDTO applicationType) {
this.applicationType = applicationType;
}
#Column(name = "description")
public String getApplicationDescription() {
return applicationDescription;
}
public void setApplicationDescription(String applicationDescription) {
this.applicationDescription = applicationDescription;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Column(name = "createdBy")
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
#Column(name = "assignedTo")
public Integer getAssignedTo() {
return assignedTo;
}
public void setAssignedTo(Integer assignedTo) {
this.assignedTo = assignedTo;
}
#Column(name = "createTime")
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#Column(name = "modifiedTime")
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
This is the Environment DTO class
#Entity
#Table(name="environment")
#org.hibernate.annotations.GenericGenerator(
name = "test-increment-strategy",
strategy = "increment")
#XmlRootElement
public class EnvironmentDTO implements Serializable {
#XmlAttribute
public Long envTypeId;
#XmlAttribute
public Long appId;
private static final long serialVersionUID = -2756426996796369998L;
private Long environmentId;
private String environmentName;
private EnvironmentTypeDTO environmentType;
private Integer owner;
private Date createTime;
private Set<InstanceDTO> instances;
private ApplicationDTO application;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "envId")
public Long getEnvironmentId() {
return environmentId;
}
private void setEnvironmentId(Long environmentId) {
this.environmentId = environmentId;
}
#Column(name = "envName")
public String getEnvironmentName() {
return environmentName;
}
public void setEnvironmentName(String environmentName) {
this.environmentName = environmentName;
}
#ManyToOne(targetEntity=EnvironmentTypeDTO.class)
#JoinColumn(name = "envType")
public EnvironmentTypeDTO getEnvironmentType() {
return environmentType;
}
public void setEnvironmentType(EnvironmentTypeDTO environmentType) {
this.environmentType = environmentType;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Temporal(TemporalType.DATE)
#Column(name = "createTime")
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#OneToMany(mappedBy="environment", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public Set<InstanceDTO> getInstances() {
return instances;
}
public void setInstances(Set<InstanceDTO> instances) {
this.instances = instances;
}
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
//#XmlTransient
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Your object graph is cyclic. There is nothing intrinsically wrong with that, and it is a natural consequence of using JPA.
Your problem is not that your object graph is cyclic, but that you are encoding it in a format which cannot handle cycles. This isn't a Hibernate question, it's a JAXB question.
My suggestion would be to stop JAXB from attempting to marshal the application property of the EnvironmentDTO class. Without that property the cyclic graph becomes a tree. You can do this by annotating that property with #XmlTransient.
(confession: i learned about this annotation by reading a blog post by Mr Doughan, which i came across after reading his answer to this question!)
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers the #XmlInverseReference extension to handle this use case. Below is an example of how to apply this mapping on two entities with a bidirectional relationship.
Customer
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
My advice is not exposing your JPA entity class to your webservices. You can create different POJO class and convert your JPA entity to the POJO. For example:
this is your JPA entity
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
you should use this class for your webservices:
public class CustomerModel{
private long id;
//you can call different WS to get the Address class, or combine to this model
public void setFromJpa(Customer customer){
this.id = customer.id;
}
}
i need some help for my class...
package com.it.ese.orbit.entity;
import javax.persistence.*;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: Shahriar Newaz
* Date: 07/03/11
* Time: 10.07
*/
#Entity
#Inheritance(strategy =InheritanceType.JOINED)
public class OrbitObject {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id",nullable = false)
private Long id;
#Column(name="Scenario",nullable = false)
private String scenario; // not sure about how to map scenario
#Column(name="code",nullable = true)
private String code;
#Column(name="name",nullable = true)
private String name;
#OneToOne(cascade=CascadeType.ALL)
private Bia bia;
#OneToOne(cascade=CascadeType.ALL)
public Impatti impatti;
#Column(name="parent",nullable = true)
#OneToMany(cascade=CascadeType.ALL)
private OrbitObject OrbitObject;
public Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getScenario() {
return scenario;
}
public void setScenario(String scenario) {
this.scenario = scenario;
}
public String getName() {
return name;
}
public void setName(String name) {
name = name;
}
// LOG
#Override
public String toString(){
return "com.it.ese.orbit.models.OrbitObject["
+ " - name="+name + " - scenario="+scenario +" - id= "+id+"]";
}
}
But i get thi error...
Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a #OneToMany, #ManyToMany or #CollectionOfElements: com.it.ese.orbit.entity.OrbitObject.OrbitObject
I wish i create an OrbitObject field as like an object of the same class...
Help please!
You either do
#Column(name="parent",nullable = true)
#ManyToOne(cascade=CascadeType.ALL)
private OrbitObject OrbitObject;
Or
#Column(name="parent",nullable = true)
#OneToMany(cascade=CascadeType.ALL)
private Set<OrbitObject> OrbitObject;
The first case implies this entity will be the owning side, namely, it will have the foreign key.
OneToMany means that OrbitObject has many OrbitObject children, which is not true because the OrbitObject property is not a collection.
You must convert it to a ManyToOne
you can use #OneToMany referring to a collection of elements, for example
#OneToMany
List<OrbitObject> orbitList;
I'm using JPA (Hibernate implementation) to save objects to the database. Selecting works fine, but for some reason, saving doesn't work. I don't get any errors, but the database doesn't get changed either. This goes for both new entities and existing ones.
EPayment pay = new EPayment();
pay.setAmount(payment.getAmount());
...
pay.setUserByToUserId(receiver);
CompayDAO.get().save(pay);
CompayDAO.save()
public void save(Object ent) {
System.out.println("Persisting: " + ent + " using " + this);
this.em.persist(ent);
}
Console output:
Opening DOA nl.compay.entities.CompayDAO#b124fa
Persisting: nl.compay.entities.EUser#1e2fe5d using nl.compay.entities.CompayDAO#b124fa
Persisting: nl.compay.entities.EUser#30b601 using nl.compay.entities.CompayDAO#b124fa
Persisting: nl.compay.entities.EPayment#ed3b53 using nl.compay.entities.CompayDAO#b124fa
Closing DOA nl.compay.entities.CompayDAO#b124fa
EPayment
package nl.compay.entities;
// Generated 21-mei-2009 12:27:07 by Hibernate Tools 3.2.2.GA
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* Payment generated by hbm2java
*/
#Entity
#Table(name = "payment", catalog = "compay")
public class EPayment implements java.io.Serializable {
private static final long serialVersionUID = -2578493336948256566L;
private Integer id;
private EUser userByToUserId;
private EUser userByFromUserId;
private String description;
private float amount;
private String method;
private Date paydate;
public EPayment() {
}
public EPayment(EUser userByToUserId, EUser userByFromUserId, float amount,
Date paydate) {
this.userByToUserId = userByToUserId;
this.userByFromUserId = userByFromUserId;
this.amount = amount;
this.paydate = paydate;
}
public EPayment(EUser userByToUserId, EUser userByFromUserId,
String description, float amount, String method, Date paydate) {
this.userByToUserId = userByToUserId;
this.userByFromUserId = userByFromUserId;
this.description = description;
this.amount = amount;
this.method = method;
this.paydate = paydate;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "to_user_id", nullable = false)
public EUser getUserByToUserId() {
return this.userByToUserId;
}
public void setUserByToUserId(EUser userByToUserId) {
this.userByToUserId = userByToUserId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "from_user_id", nullable = false)
public EUser getUserByFromUserId() {
return this.userByFromUserId;
}
public void setUserByFromUserId(EUser userByFromUserId) {
this.userByFromUserId = userByFromUserId;
}
#Column(name = "description", length = 1024)
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
#Column(name = "amount", nullable = false, precision = 8)
public float getAmount() {
return this.amount;
}
public void setAmount(float amount) {
this.amount = amount;
}
#Column(name = "method", length = 50)
public String getMethod() {
return this.method;
}
public void setMethod(String method) {
this.method = method;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "paydate", nullable = false, length = 0)
public Date getPaydate() {
return this.paydate;
}
public void setPaydate(Date paydate) {
this.paydate = paydate;
}
}
As Sherkaner mentioned, a save doesn't result in an INSERT or UPDATE directly. You have to flush the session or - better in my opinion - close the unit of work / commit the transaction. You do have transactions?
use #Transactional on your method.....
#Transactional
public void save(Object ent){
.....
.....
}
The program doesn't have to sync with the database right away, have you tried this.em.flush(); somewhere?
Don't think this as bug in Hibernate implementation.This is desired behavior,you would like to have minimum communication with database so Hibernate(or any good ORM framework) will consolidate all your changes and will flush your changes in one go.