Persistense #OneToMany - #ManyToOne - java

I'm having trouble persists the following entities:
#Entity
#Table(name="entityOne")
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne")
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
In EntityOneDAO:
em.merge(entityOne);
And it is only persisted to EntityOne and not the list of EntityTwo ... How do I persist the list ?
Thanks all

You need to take care of both:
transitive persistence (using Cascade)
synchronizing both end of the bi-directional association.
So EntityOne should Cascade Persist and Merge to EntityTwo:
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne", cascade = { CascadeType.PERSIST, CascadeType.MERGE})
private List<EntityTwo> entities = new ArrayList<>();
As you can see, you should always initialize your collection classes to avoid unnecessary null checks.
And it's always better to add the following helper child adding utility in your parent classes (e.g. EntityOne)
public void addChild(EntityTwo child) {
if(child != null) {
entities.add(child);
child.setEntityOne(this);
}
}
Then you can simply call:
EntityOne entityOne = new EntityOne();
entityOne.setProperty("Some Value");
EntityTwo entityTwo_1 = new EntityTwo();
entityTwo_1.setName("Something");
EntityTwo entityTwo_2 = new EntityTwo();
entityTwo_2.setName("Something");
entityOne.addChild(entityTwo_1);
entityOne.addChild(entityTwo_2);
entityManager.persist(entityOne);
P.S.
Please remove the #Inject annotation from the EntityTwo class. Entities are not Components.
And persist is much more efficient than merge, when you want to insert new entities.

You should explicitly set each entityTwo objects' entityOne field.
Such that:
entityTwo_1.setEntityOne(entityOne);
entityTwo_2.setEntityOne(entityOne);
entityOne.entities.add(entityTwo_1);
entityOne.entities.add(entityTwo_2);
em.merge(entityOne);

Try this:
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne",
cascade = { CascadeType.ALL})
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
You can read here, about the CascadeType.
edited.

Related

save/create Entity in Hibernate

I am facing an issue with persisting Observation entity.
The Observation entity has a list of protocols and every protocol has 1 observer. The observer can also be created before or not, so I can have many protocols which have created observers and many protocols which have not created observers.
The desired behavior for me is if I create this observation then observers with id == null will be created and observers with id will be merged.
The issue I am facing is if I specify CascadeType.Merge I can create observation with only the observers which were created before and if I specify CascadeType.Persist/CascadeType.All, I can create observation with only the observers that were not created before.
ObservationEntity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Observation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "observation_id", referencedColumnName = "id", nullable = false)
#OrderBy("observer ASC")
#Fetch(FetchMode.SUBSELECT)
private List<Protocol> protocols;
}
ProtocolEntity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Protocol {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
#JoinColumn(name = "observer_id", nullable = false)
private Observer observer;
}
ObserverEntity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Observer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
private String imoCode;
}
ObservationService:
#Service
public class ObservationService {
#Autowired private ObservationMapper observationMapper;
#Autowired private ObservationRepository observationRepository;
public ObservationDTO create(ObservationDTO observationDTO) {
Observation observationCreated = observationRepository.save(observationMapper.observationDTO2Observation(observationDTO));
return observationMapper.observation2ObservationDTO(observationCreated);
}
}
Error:
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : sk.adambarca.serverspringboot.model.entity.Interval.protocol -> sk.adambarca.serverspringboot.model.entity.Protocol
Try this:
private List<Protocol> protocols = new ArrayList<>();

JPA Entity Class using class object as parameter

I have a class User which has a parameter of another class type ShoppingList.
As this...
#Entity
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private Long id;
private String Name;
#?????
private ShoppingList[] shoppingList;
}
How can i make this ManyToOne relationship while the variable being an array?
The idea is to have a User table and another ShoppingList table, so the user can have multiple lists at the same time.
This would be the correct way:
One Employee has many ShoppingLists.
One ShoppingList has only one Employee.
#Entity
public class Employee implements Serializable {
....
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<ShoppingList> shoppingList;
....
}
#Entity
public class ShoppingList implements Serializable {
....
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
....
}
You can fine-tune your entities as per your need.
For more info, I would refer to this tutorial, it has helped me a lot.

Delete both parent and child in #OneToMany relationship

I have an entity called itineraryTraveller, and every itineraryTraveller can have many flightEntity. When I try to delete an itineraryTraveller (parent), from the database, I get this error message:
a foreign key constraint fails (`pquino01db`.`ITINERARYTRAVELLER_FLIGHTENTITY`, CONSTRAINT `FK_ITINERARYTRAVELLER_FLIGHTENTITY_flights_ID` FOREIGN KEY (`flights_ID`) REFERENCES `FLIGHTENTITY` (`ID`))"
Here is my itineraryTraveller entity:
#Entity
public class itineraryTraveller implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<flightEntity> flights;
#Temporal(javax.persistence.TemporalType.DATE)
private Date departureDate;
private String departureLocation;
private String arrivalLocation;
private double cost;
private char status;
private ArrayList<String> stops;
private String stopPrint;
private String userName;
private int iden;
// ...
}
And the flightEntity looks like this:
#Entity
public class flightEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Temporal(javax.persistence.TemporalType.DATE)
private Date departureDate;
private String airlineCode;
private String flightNumber;
private String departureLocation;
private String arrivalLocation;
private double businessCost;
private double economyCost;
private int numBusinessSeats;
private int numEconomySeats;
// ...
}
Can someone see the problem? I think my #OneToMany annotation might be missing something, but I'm not sure what. I want to delete both the parent and child at the same time.
Your relationship between the two entities is unidirectional as there is no mapping from flightEntity back to itineraryTraveller entity as you do not have a #JoinColumn on your flightEntity. There can be one of the following solutions for your problem:
Add a #ManyToOne annotation on the flightEntity as follows:
#Entity
public class flightEntity implements Serializable {
// ....
#ManyToOne
#JoinColumn(name="<name_of_foreignkey_column>")
private itineraryTraveller traveller;
// ...
}
And you have to add a mappedBy attribute to your #OneToMany annotation:
#OneToMany(mappedBy="traveller", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
Thereby making the relationship between the entities bidirectional.
This one can solve the problem if you already have tables in the database with a foreign key relationship.
Use #JoinTable annotation on the #OneToMany annotation:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name="<join_table_name>", joinColumns=#JoinColumn("TRAVELLER_ID"), inverseJoinColumns=#JoinColumn("FLIGHT_ID"))
private List<flightEntity> flights;
(The names of the columns are considered to be examples, and can be changed.)
This last mapping is useful if you don't have tables in the database with foreign key column defined, and it will create a new table as an association between the tables; which is normally the case in a many-to-many relationships.
If it is possible use #ManyToOne annotation on the flights entity. This is normal way of mapping a one-to-many relationships.
Lastly, there are conventions in Java that state class names should begin with a capital letter. So I would rename the entity names to Flight and ItineraryTraveller.
Note that in some cases the #JoinColumn on the child object must have insertable = false and updatable = false like this:
#JoinColumn(name = "user_id", insertable = false, updatable = false)
public class User {
private List<UserRole> roles;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "user")
public List<UserRole> getRoles() {
return this.roles;
}
public void setRoles(List<UserRole> roles) {
this.roles = roles;
}
}
public class UserRole {
#ManyToOne
#JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
}

Hibernate #ManyToOne cascade children constraint exception

Im facing a little problem here.
I have two entities: Parent and Child, Parent has a List annotated #OneToMany.
The problem is when I try to insert a new Parent, it crashes when persisting the children, because the Parent Id was not generated yet.
Is that a fix for it?
#Entity
#Table(name = "PRODUTO")
public class Parent extends BaseEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID_PRODUTO")
private Integer produtoId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "produtoId", orphanRemoval = true)
// #JoinTable(name = "PRODUTO_TAMANHO", joinColumns = #JoinColumn(name = "ID_PRODUTO"))
#OrderBy("preco ASC")
private List<Child> children;
}
#Entity
#IdClass(Child.PrimaryKey.class)
#Table(name = "PRODUTO_TAMANHO")
public class Child extends BaseEntity
{
public static class PrimaryKey extends BaseEntity
{
private static final long serialVersionUID = -2697749220510151526L;
private Integer parentId;
private String tamanho;
//rest of implementation
}
#Id
#Column(name = "ID_PRODUTO")
private Integer parentId;
#Id
#Column(name = "TAMANHO")
private String tamanho;
#ManyToOne
#JoinColumn(name = "ID_PRODUTO", insertable = false, updatable = false)
private Parent parent;
}
I think if I persist firstly the parent, than persist the children would be a bad approach.
Is that a way to persist the children, when persisting Parent?
Thanks!
Guys, the exception that occurs when persisting Parent is:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'ID_PRODUTO' cannot be null
I found a guy facing the same problem: #OneToMany and composite primary keys? (maybe it's better explained)
Here is my insertion code
Parent parent = new Parent();
Child child1 = new Child();
child1.setTamanho("Tamanho 1");
child1.setParent(parent);
Child child2 = new Child();
child2.setTamanho("Tamanho 1");
child2.setParent(parent);
List<Child> children = parent.getChildren();
children.add(child1);
children.add(child2);
save(parent);
//all of this instances, is coming from a view.jsp binded by spring, I can confirm it is exactly like this, with parentId as null
//when updating, it goes perfectly
There are few problems with your entity class.
mappedBy attribute in Parent entity should be set to parent: mappedBy="parent".
In child entity, below field is not required.
#Id
#Column(name = "ID_PRODUTO", nullable = true)
private Integer parentId;
Updated entity is like this.
#Entity
#Table(name = "PRODUTO")
public class Parent extends BaseEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID_PRODUTO")
private Integer produtoId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
// #JoinTable(name = "PRODUTO_TAMANHO", joinColumns = #JoinColumn(name = "ID_PRODUTO"))
#OrderBy("preco ASC")
private List<Child> children;
}
#Entity
#IdClass(Child.PrimaryKey.class)
#Table(name = "PRODUTO_TAMANHO")
public class Child extends BaseEntity
{
public static class PrimaryKey extends BaseEntity
{
private static final long serialVersionUID = -2697749220510151526L;
private Integer parentId;
private String tamanho;
//rest of implementation
}
/* #Id
#Column(name = "ID_PRODUTO", nullable = true)
private Integer parentId; */ // Not required.
#Id
#Column(name = "TAMANHO")
private String tamanho;
#ManyToOne
#JoinColumn(name = "ID_PRODUTO", insertable = false, updatable = false)
private Parent parent;
}
Also I do not understand child inner class for primary key. Use proper primary as you have used parent.
And while inserting set both parent to child and child to parent. See my blog for more details.Here

#OneToOne relationship with additional constraint

Suppose, we have two entities, first one:
#Entity
#Table(name = "entitya")
public class EntityA {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private Long name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<EntityB> childEntities;
}
and the second:
#Entity
#Table(name = "entityb")
public class EntityB {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "master")
private Boolean master;
#ManyToOne
#JoinColumn(name = "parent")
private EntityA parent;
}
So far, so good. However underlying database tables and constrains enforce that for any entityA there can be only one EntityB with boolean field master set to true. I can extract it by adding following method to entityA:
public entityB getMasterChild() {
for(entityB ent : childEntities) {
if(ent.isMaster()) {
return ent;
}
}
}
The question is, can I create #OneToOne relationship in EntityA that can express that rule, so that entityA can have additional masterChild member of type entityB?
If I understood you correctly you want to create/define a relationship between two entities based on a value of some entity's property. The think is that relationship between entities is defined on entities count (how many entities can has the other entity) and not on some entity's property value.
However
If you really want to use #OneToOne mapping for masterChild I would recommend creating a separate table/entity for it. Once this is done, you can include this new MasterChild entity into EntityA and annotate it with #OneToOne.
Here is new MasterChild entity
#Entity
public class MasterChild extends EntityB{
#Id
#Column(name = "id")
private Long id;
}
Note that I have removed 'master' from EntityB as it is no longer needed
#Entity
#Table(name = "entityb")
public class EntityB {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "parent")
private EntityA parent;
}
And here is modified EntityA
#Entity
#Table(name = "entitya")
public class EntityA {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private Long name;
#OneToOne
private MasterChild master;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<EntityB> childEntities;
}

Categories

Resources