I have two entities, a Competition and a Team:
Competition:
#Entity
public class Team implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int id;
#Column(length=255)
String name;
#ManyToOne(optional=true)
Competition nextCompetitionAttending;
List<SiteUser> teamMembers;
nextComeptitionAttending is set by the following method:
public void setNextCompetitionAttending(Competition nextCompetitionAttending) {
this.nextCompetitionAttending = nextCompetitionAttending;
nextCompetitionAttending.addTeam(this);
}
Competition is defined thusly:
#Entity
public class Competition implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
int maxTeamSize;
String name;
#OneToMany(mappedBy="nextCompetitionAttending" )
List<Team> teamsAttending;
My persistance code looks like this:
utx.begin();
EntityManager em = emf.createEntityManager();
em.merge(user);
Competition c = em.find(Competition.class, id);
Team teamon = user.getTeamOn();
teamon.setNextCompetitionAttending(c);
System.out.println("Set next");
em.flush();
utx.commit();
em = emf.createEntityManager();
Team t = em.find(Team.class, teamon.getId());
System.out.println(t.getNextCompetitionAttending());
In the database, the column "NEXTCOMPETITIONATTENDING_ID" is null, as is the println at the end of the code which I've been using to try to debug this.
Why doesn't this persist?
The culprit is probably this line:
em.merge(user);
user is a detached entity, and you merge it. Then, you get the team from the user. But merge doesn't make the detached entity attached. It loads the attached user, copy the state from the detached one to the attached one, and returns the attached one. So, all the subsequent changes you're doing are done to the detached entity.
Replace this line with
user = em.merge(user);
Related
I am completely new to JPA and ORM concept, so I am hoping someone can lucidly explain what might be the problem with my code.
#Entity
#Table(name = "PERSISTENCE_customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1005220876458L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<CustomerOrder> orders;
}
#Entity
#Table(name = "PERSISTENCE_ORDER")
public class CustomerOrder implements Serializable{
private static final long serialVersionUID = 199102142021L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
String status;
#NotNull
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<LineItem> lineItems = new ArrayList();
#NotNull
private String orderNumber;
................
................
}
#Entity
#Table(name = "PERSISTENCE_LINEITEM")
public class LineItem implements Serializable {
private static final long serialVersionUID = 1991217202100959L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private Integer quantity;
#NotNull
private Part part;
}
Initially, the Customer entity is created through the user interface and persisted successfully. Later, the customer has an order and I update the Customer with CustomerOrder as follow:
private void UpdateCustomer(Customer customer) {
FacesContext fc = FacesContext.getCurrentInstance();
List<ShoppingCartItem> shoppingCart = getShoppingCart();
CustomerOrder order = new CustomerOrder();
List<CustomerOrder> orders = customer.getOrders();
order.setLastUpdated(new Date());
order.setOrderNumber(getInvoiceNumber());
List<LineItem> lineItems = shoppingCart
.stream()
.map(e -> (new LineItem(e.getPart(), e.getQuantity())))
.collect(Collectors.toList());
order.setLineItems(lineItems);
order.setStatus("Pending Shipment");
order.setTotal(getTotal());
orders.add(order);
customer.setOrders(orders);
try {
updateOrders(customer, orders);
fc.addMessage(null,
new FacesMessage("Customer order added successfuly"));
} catch (ListServiceException e) {
FacesMessage errMsg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Error while adding customer order: ", e.getMessage());
fc.addMessage(null, errMsg);
}
}
private void updateOrders(Customer cust, List<CustomerOrder> orders) throws ListServiceException {
try { //em is the EntityManager injected as the field member
if (em != null) {
if (em.isOpen()) {
Customer c = getCustomer(cust.getId());
c.setOrders(orders);
em.merge(c);
} else {
logger.severe("Entity manager is closed");
}
else {
logger.severe("Entity manager is NULL");
}
} catch (Exception e) {
throw ThrowListServiceException.wrapException(e);
}
}
Once the EntityManage merges I get the following exception. I was under the impression that I don't need to explicitly persist the LineItem and CustomerOrder entities myself. I thought that the JPA will persist all the entities in the object graph. Why am I getting this exception? (I am using GlassFish 5.1 server with EclipseLink JPA)
Thanks in advance.
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Error Code: 30000
Call: INSERT INTO PERSISTENCE_CUSTOMER_PERSISTENCE_ORDER (orders_ID, Customer_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="orders" sql="INSERT INTO PERSISTENCE_USER_PERSISTENCE_ORDER (orders_ID, User_ID) VALUES (?, ?)")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:905)
...............................
.................................
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Update
Using the IDE (Netbeans) debugger, I stepped through the code, and as I predicted, during the entity merge the JPA does not add new entities that are part of the object graph to the persistence context. For instance, in the updateOrders() method when I try to update the existing Customer object with a list of new CustomerOrder object, the JPA doesn't figure out that the elements in the List are not part of the persistence context yet and they need to be added. As a result, I had to modify my code to first add the List to the persistence context and then merge the Customer object with the newly persisted List. By doing so I no longer get the exception.
By the way, at moment, all the mapping relationships are unidirectional because I didn't see any reasons to use bidirectional mapping. However, would I gain anything by making these mappings bidirectionals?
Your OneToMany mapping is missing join specification or mappedBy value
I noticed that.
Firstly You should commit new order to database.Then you should link it with user.I'm not sure if this solves your problem but this is a problem.Can you check it ?
In my opinion, if you keep customer information in your Order entity, it may solve this problem.
#ManyToOne ()
private Customer customer;
And in your Customer entity you should put mappedBy=customer for orders field.
After that, instead of giving orders for customer, you can give customer for a specific order. In my opinion it will achieve a better relationship mapping;
order.setCustomer(customer);
I hope i understood it right and this will solve your problem. When you give customer detail for order, you dont need to give orderlist detail for the same customer. Only one of them should be enough.
I want to get the children collection of the owner entity of a one to many relationship.
I have those two entities :
#Entity
#Table(name = "commande")
public class Commande implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "commande")
#JoinColumn(name= "papa_id")
#JsonIgnore
private Set<Piece> pieces = new HashSet<>();
#Entity
#Table(name = "piece")
public class Piece implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "ref")
private String ref;
#ManyToOne
private Commande commande;
And the resource :
#RequestMapping(value = "/papas/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
#Transactional
public ResponseEntity<Papa> getPapa(#PathVariable Long id) {
log.debug("REST request to get Papa : {}", id);
Papa papa = papaRepository.findOne(id);
papa.getEnfants().size();
...
}
I had to put #Transactional in order to make the .size() work (otherwise I have an exception).
It works.
But if in another method I delete one Enfant entity, then if I call getPapa again, I am getting an error durint .size() :
Unable to find com.myapp.stagiaireproject.domain.Enfant with id 3
Is it a problem of a transaction not closed?
For the first time, move all work with repository to service and mark this method as #Transactional. Set #Transactional annotation to controller method is bad practice.
One-to-many annotation is lazy by default, you can explicity set #OneToMany(mappedBy = "commande", fetch = FetchType.EAGER)
to told hibernate to read collection from database when fetching this entity.
Returning to you question: if you done this is one transaction, hibernate uses fist level cache (wich is not disabled), i.e. it caches entities, which loads by PK (id) during the transaction. And if you moves all work with repository to service class, your transaction commited before you return data from controller, and next request will read data from database, not from cache.
I am doing Many To One relationship using JPA . While deleting child object from Child table it's throwing exception.
Below is My code:
Project.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="projectName")
private String projectName;
#Column(name="projectDesc")
private String projectDesc;
#ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="companyId")
Company.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="compName")
private String compName;
#Column(name="address")
private String address;
Below is Insert code:
InserAction.java
public static void main(String[] args) {
Company comp2 = new Company();
comp2.setCompName("IBM");
comp2.setAddress("Bangalore");
Project pro2 = new Project();
pro2.setProjectName("Management System");
pro2.setProjectDesc("System");
pro2.setCompany(comp2);
EntityManager entityManager = EntityManagerUtil.getEmf().createEntityManager();
try{
EntityTransaction entr = entityManager.getTransaction();
entr.begin();
entityManager.persist(pro2);
entr.commit();
}
}
DeleteAction.java
EntityManager entityManager = EntityManagerUtil.getEmf()
.createEntityManager();
try {
EntityTransaction entr = entityManager.getTransaction();
entr.begin();
Project project = entityManager.find(Project.class,5);
entityManager.remove(project);
entr.commit();
}
Exception is
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`prabha`.`project`, CONSTRAINT `FK_project_companyId` FOREIGN KEY (`companyId`) REFERENCES `company` (`id`))
Error Code: 1451
Call: DELETE FROM company WHERE (id = ?)
bind => [1 parameter bound]
Query: DeleteObjectQuery(com.demo.manytoone.Company#301db5ec)
While deleting project object from Project table it' throwing above exception how can I over come this.
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST}, fetch=FetchType.EAGER)
The above code will resolve your issue. If you observe source code of annotation ManyToOne it has an array of cascade type so you can map multiple cascade types
You should not use CascadeType.ALL, try using CascadeType.MERGE
The meaning of CascadeType.ALL is that the persistence will propagate (cascade) all EntityManager operations (PERSIST, REMOVE, REFRESH, MERGE, DETACH) to the relating entities.
It seems in your case to be a bad idea, as removing an Project would lead to removing the related Company when you are using CAscadeType.ALL. As a Company can have multiple projects, the other projects would become orphans. However the inverse case (removing the Company) would make sense - it is safe to propagate the removal of all projects belonging to a Company if this company is deleted.
You can also use various CascadeTypes, for e.g.
cascade = {CascadeType.PERSIST,CascadeType.MERGE}.
So use all those that applied to you.
For more info.
You got MySQLIntegrityConstraintViolationException. It means there are tables in the database you're binding. You should map when you set up the many to one relationship tables. Company should be able to get more than one project. So you have to define the list of projects.
Project.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="projectName")
private String projectName;
#Column(name="projectDesc")
private String projectDesc;
#ManyToOne
#JoinColumn(name="companyId")
private Company company;
Company.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="compName")
private String compName;
#Column(name="address")
private String address;
#OneToMany(mappedBy="company", fetch=FetchType.EAGER)
private List<Project> projects;
I have two entities.
Sports:
#Entity
public class Sports implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "SPORT_NAME")
private String name;
...
}
and SportFacility:
#Entity
public class SportFacility implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "SPORTFAC_NAME")
private String name;
#OneToMany
private List<Sports> sports;
....
}
Then I have some created sports in Sports table, for example:
--id-----name-----
- 0 - Football -
- 1 - Basketball-
- 2 - Golf -
------------------
And I need to create sport facility with some sports.Lets take all sports
List<Sports> listSports = sb.getAllSports(); //JPQL: SELECT s FROM Sports s
SportFacility sportFacility = new SportFacility("Stadium", listSports);
em.getTransaction().begin();
em.persist(sportFacility);
em.getTransaction().commit();
Error:
java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: database.entities.sports[ id=0 ].
I don' want to use Cascade.ALL or PERSIST becase there are only few sports in table and I always don't need to create new record with same sport...
Thank you.
The exception is related to the fact that collection of Sports that you retrieved are no longer in the persistence context when you try to persist the SportFacility - persistence context is by default transaction scoped (created when transaction begins and destroyed when it ends) - and EL doesn't know what to do with objects it doesn't control.
Move
em.getTransaction().begin();
to the beginning.
What does,
sb.getAllSports();
do? Does it use the same EntityManager, or a different one?
database.entities.sports[ id=0 ]
The error seems to indicate that one of your sports has a 0 id, this seems very odd? Did you persist the sports correctly?
My entities are:
the ID of device which is deiveID
has many-to-many relationship with
the ID of Lib which is rID
my test code is :
two new device entities want to set the same new libentity
the problem is :
if i use the same entitymanager to persist that 2 new device entities, it will be ok.
but if i use 2 different entitymanager instance to persist them ,the error"primary key violation" will come out. I think the entitymanger try to persist the libentity at the second time, which has already been persisted in the first time.
--------------deviceinfo entity ------------------------------------------------
#Entity
#Table(name="deviceInfo")
public class DeviceInfoEntity extends BaseEntity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long deviceId;
....
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinTable(name = "device_lib", joinColumns = #JoinColumn(name = "deviceInfo_id",
referencedColumnName="deviceId"),
inverseJoinColumns = #JoinColumn(name = "lib_id", referencedColumnName="rId"))
private List<LibEntity> resourceList = null;
......
}
-------------------------lib entity ---------------------------------------------
#Entity
#Table(name="lib")
public class LibEntity extends BaseEntity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long rId;
#ManyToMany(mappedBy = "resourceList", cascade=CascadeType.ALL,
fetch=FetchType.LAZY, targetEntity=DeviceInfoEntity.class)
private List<DeviceInfoEntity> deviceInfolist = null;
.....
}
my test code is:
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("testPU");
EntityManager em = emFactory.createEntityManager();
LibEntity libEntity = new LibEntity();
DeviceInfoEntity dEntity = new DeviceInfoEntity();
dEntity.setName("dadadada");
dEntity.setLibEntity(libEntity);
DeviceInfoEntity dEntity2 = new DeviceInfoEntity();
dEntity2.setName("dadadadadddddd");
dEntity2.setLibEntity(libEntity);
em.getTransaction().begin();
em.persist(dEntity);
em.getTransaction().commit();
em.close();
EntityManager em2 = emFactory.createEntityManager();
em2.getTransaction().begin();
em2.persist(dEntity2);
em2.getTransaction().commit();
it will have the error:
Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.LIB(RID)"; SQL statement:
INSERT INTO lib (RID) VALUES (?) [23505-165]
but if i use the same EntityManager the error will not happen. Is there anyone know whats the reason? is that caused by cascade=CascadeType.ALL?
You are corrupting your persistence context by assign detached objects to managed objects. Managed object should only reference other managed objects.
For dEntity2 you should set the libEntity to the result of a find(), getReference() or merge() of libEntity.
i.e.
dEntity2.setLibEntity(em2.find(libEntity.getClass(), libEntity.getId());
You could probably also call merge() instead of persist() and it should resolve the detached objects.