I am having some trouble understanding what I'm meant to do in the face of the following error: javax.ejb.EJBException: java.lang.IllegalStateException: Multiple representations of the same entity
I have code that's something like this:
#Entity
#Table(name="Survey", schema="dbo")
public class Survey {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column
private String line;
#OneToMany(mappedBy = "survey", cascade=CascadeType.ALL, orphanRemoval = true)
private List<Section> sections;
// getters and setters
...
public boolean equals(Survey other) {
if(other == null) {
return false;
} else if (other.id != id) {
return false;
} else if (!other.line.equals(line)) {
return false;
}
return true;
}
}
#Entity
#Table(name="Section")
public class Section {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="surveyId")
private Survey survey;
#Column
private String sectionName;
}
Issue is, when I call em.merge on a Survey instance, say s1, I get the above Multiple representations error, where one of the representations is s1, and the other is the survey of one of the Section instances within s1's sections list.
From my perspective, these two representations seem identical, and should refer to the same object. How should I setup the relationships between these two classes so that all of the sections in a given Survey refer to the same Survey object?
Related
I am trying to join a column using the #JoinColumn annotation but my column is always returning a null and I am not sure why.
#Entity
public class Blender implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "blender_id")
private int id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "blender", fetch = FetchType.EAGER)
private List<Ingredients> ingredients;
private Status status;
private String type;
public Blender() {
}
public Blender(List<Ingredients> ingredients) {
this.ingredients = ingredients;
}
public List<Ingredients> getIngredients() {
return ingredients;
}
public void setIngredients(List<Ingredients> ingredients) {
this.ingredients = ingredients;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public int getId() {
return id;
}
#Override
public String toString() {
String result = String.format(
"Blender[id=%d, type=%s, status=%s]%n",id,type,status);
if(ingredients!=null){
for (Ingredients ingredient: ingredients) {
result += String.format(
"ingredients[id=%d,fruit=%s,juice=%s,ice=%s]%n",
ingredient.getId(),
ingredient.getFruit(),
ingredient.getJuice(),
ingredient.getIce());
}
}
return result;
}
}
and Ingredients
#Entity
public class Ingredients implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private int fruit;
private int juice;
private int ice;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(columnDefinition="integer", name = "blender_id")
private Blender blender;
public Ingredients() {
}
public Long getId() {
return id;
}
public int getFruit() {
return fruit;
}
public void setFruit(int fruit) {
this.fruit = fruit;
}
public int getJuice() {
return juice;
}
public void setJuice(int juice) {
this.juice = juice;
}
public int getIce() {
return ice;
}
public void setIce(int ice) {
this.ice = ice;
}
public Blender getBlender() {
return blender;
}
public void setBlender(Blender blender) {
this.blender = blender;
}
#Override
public String toString() {
return "Ingredients{" +
"id=" + id +
", fruit='" + fruit + '\'' +
", juice='" + juice + '\'' +
", ice='" + ice + '\'' +
'}';
}
}
#JoinColumn(columnDefinition="integer", name = "blender_id") is returning null not sure why.
try with just
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "blender_id")
private Blender blender;
#OneToMany(mappedBy = "association", cascade = { CascadeType.ALL })
private List<Company> company;
#ManyToOne
#JoinColumn(name = "association_id")
private Association association;
You can try this pattern.
Read for you.
How to Enable Lazy Loading in Hibernate
Before moving further, it is important to recap the default behavior of lazy loading in case of using hibernate mappings vs annotations.
The default behavior is to load ‘property values eagerly’ and to load ‘collections lazily’. Contrary to what you might remember if you have used plain Hibernate 2 (mapping files) before, where all references (including collections) are loaded eagerly by default. Also note that #OneToMany and #ManyToMany associations are defaulted to LAZY loading; and #OneToOne and #ManyToOne are defaulted to EAGER loading. This is important to remember to avoid any pitfall in future.
To enable lazy loading explicitly you must use "fetch = FetchType.LAZY" on a association which you want to lazy load when you are using hibernate annotations.
An example usage will look like this:
#OneToMany( mappedBy = "category", fetch = FetchType.LAZY ) private Set products; Another attribute parallel to "FetchType.LAZY" is "FetchType.EAGER" which is just opposite to LAZY i.e. it will load association entity as well when owner entity is fetched first time.
How Lazy Loading Works in Hibernate
The simplest way that Hibernate can apply lazy load behavior upon your entities and associations is by providing a proxy implementation of them. Hibernate intercepts calls to the entity by substituting a proxy for it derived from the entity’s class. Where the requested information is missing, it will be loaded from the database before control is ceded to the parent entity’s implementation.
Please note that when the association is represented as a collection class, then a wrapper (essentially a proxy for the collection, rather than for the entities that it contains) is created and substituted for the original collection. When you access this collection proxy then what you get inside returned proxy collection are not proxy entities; rather they are actual entities. You need not to put much pressure on understanding this concept because on runtime it hardly matters.
I am trying to establish the aggregation relationship between two Java classes through JPA annotations in order to persist them into a database.
public class Ticket
{
private String ticketNo;
private Date releasedDate;
private boolean printed;
}
public class Discount
{
private String percentage;
private Date releasedDate;
private boolean printed;
}
Such as mentioned here, the aggregation relationship is unidirectional and thus, only it is necessary to map one side. From the solution given by this page, I think the solution will be:
public class Discount
{
private String percentage;
private Date releasedDate;
private boolean printed;
#ManyToOne(name="TICKET_ID")
private Ticket ticket;
}
However, in some examples of aggregation, the many side class appears inside the one side class. Thus, I am considering this too:
public class Ticket
{
private String ticketNo;
private Date releasedDate;
private boolean printed;
#OneToMany(mappedBy="ticket")
private List<Discount> discounts = new ArrayList<Discount>();
}
Which option is the proper one?
This how you map a unidirectional many-to-one relationship:
#Entity
public class Ticket {
#Id
#GeneratedValue
private Long id;
private String ticketNo;
private Date releasedDate;
private boolean printed;
// getters and setters
}
#Entity
public class Discount {
#Id
#GeneratedValue
private Long id;
private String percentage;
private Date releasedDate;
private boolean printed;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "TICKET_ID") // you can rename the join column
private Ticket ticket;
// getters and setters
}
Note:
JoinColumn (foreign key in the database terminology) must be on the many side of the relationship (this is the Discount in your case).
The #Id annotations are also mandatory. In this case, the ID will be generated by the persistence provider automatically. If you are using database sequence or table or some other strategy you can redefine it.
That looks right to me. A discount has a ticket. You could also include the discounts accessible from the tickets like ticket.getDiscounts() if you need to access them in a query such as SELECT t FROM Ticket t WHERE t.discounts.percentage >= :discountPercentage.
#Entity
public class Ticket {
#Id
private String ticketNo;
private Date releasedDate;
private boolean printed;
#OneToMany(mappedBy = "ticket", fetch = FetchType.LAZY)
private List<Discounts> discounts;
}
#Entity
public class Discount {
private String percentage;
private Date releasedDate;
private boolean printed;
#ManytoOne(name="TICKET_ID")
private Ticket ticket;
}
However, I wouldn't recommend using #OneToMany as this can create problems serializing too much data to JSON if you are returning this as JSON results or just lazily loading too much data by accident. You should always be able to work with just #ManyToOne as an example if you did not put the #OneToMany association query can be SELECT t FROM Discount d INNER JOIN d.ticket t WHERE d.percentage >= :discountPercentage
pals.
I have an issue with Hibernate's JPA implementation. I use spring-boot-starter-data-jpa and PostgreSql v9.
I have two entities with bidirectional connection via OneToMany & ManyToOne:
#Entity
public class ShoppingCart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "shoppingCart", cascade = {CascadeType.ALL})
private List<Good> goods = new ArrayList<>();
public void addGood(Good good) {
good.setShoppingCart(this);
goods.add(good);
}
public Good removeGood(Good good) {
goods.remove(good);
good.setShoppingCart(null);
return good;
}
public ShoppingCart() {
}
public List<Good> getGoods() {
return goods;
}
public ShoppingCart(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
And second entity is
#Entity
public class Good {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cart_id")
#JsonIgnore
private ShoppingCart shoppingCart;
public ShoppingCart getShoppingCart() {
return shoppingCart;
}
public void setShoppingCart(ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
public Good(String name) {
this.name = name;
}
public Good() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Also I use CrudRepository to access ShoppingCart
public interface ShoppingCartRepository extends CrudRepository<ShoppingCart, Long> {}
And when I'm trying to fill existing cart I have two goods in my database. This is a code to add some goods into existing cart:
ShoppingCart cart = shoppingCartRepository.findOne(id);
cart.addGood(new Good("Butter"));
return shoppingCartRepository.save(cart);
In table "good" I have now two elements with different PKey and same data
5;"Butter";100
6;"Butter";100
Why it happens?
Also, when I'm trying to insert breakpoint at repository.save line, I see only one good in goods list in cart.
So, the problem is solved.
First way to solve is to make method with save code #Transactional.
Secon way is to use getGoods() instead of goods. We should change this code
public void addGood(Good good) {
good.setShoppingCart(this);
goods.add(good);
}
public Good removeGood(Good good) {
goods.remove(good);
good.setShoppingCart(null);
return good;
}
to this
public void addGood(Good good) {
good.setShoppingCart(this);
this.getGoods().add(good);
}
public Good removeGood(Good good) {
this.getGoods().remove(good);
good.setShoppingCart(null);
return good;
}
getGoods() here forces hibernate to update state of object and everything works fine.
As for me, I use both ways together
It happens because you create a new Good object without id. So Hibernate will generate a new id and persist the new object. If you don't want to create a new object, but only assign an already existing one, you either have to fetch the existing one from the database and assign it to the ShoppingCart oder add the ID if you create the new Good object.
I am trying to develop a web application and I was wondering if there is a way to utilize a foreign key without writing a lot of code.
My Trainees.java
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#ManyToOne
#JoinColumn(name = "status_trainee")
private String status_TraineeID;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
public Trainees(){
}
public Trainees(String name, String surname, String phoneDetails, String email, int id, int groupsID, String status_TraineeID, int customersID) {
super();
this.name = name;
this.surname = surname;
this.email = email;
this.phoneDetails = phoneDetails;
this.groupsID = groupsID;
this.status_TraineeID = status_TraineeID;
this.customersID = customersID;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Trainees){
Trainees contact = (Trainees) object;
return contact.traineesID == traineesID;
}
return false;
}
#Override
public int hashCode() {
return traineesID;
}
}
Status_Trainee.java
#Entity
public class Status_Trainee {
#Id
#GeneratedValue
private int status_traineeID;
private String value;
public Status_Trainee(){
}
public Status_Trainee(String value, int id) {
super();
this.value = value;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Status_Trainee){
Status_Trainee value = (Status_Trainee) object;
return value.status_traineeID == status_traineeID;
}
return false;
}
#Override
public int hashCode() {
return status_traineeID;
}
}
Error: Caused by: org.hibernate.AnnotationException: #OneToOne or #ManyToOne on uaiContacts.model.Trainees.status_TraineeID references an unknown entity: String
So my aim is that using the Trainees table and class, I could retrieve the value of Status_Trainee table using the foreign key. For example: if foreign keys ID is 2, then it would retrieve a string from status_trainee table where primary key would match the foreign key ID.
I am using models, controlers, hibernate and angularjs to display to the view, I don't really want to pass the table through all this, I thought using something like ManyToOne or JoinColumns would retrieve the value?
Thanks for all the help!
You should add a reference to StatusTrainee in Trainee and annotate that with OneToMany, ManyToOne or OneToOne. Depending on which kind of relationship you will need a list of StatusTrainee or just a StatusTrainee.
Tip: dont use underscores in class names.
First of all, it is not recommended to use "_" in a class name when using hibernate. Hibernate uses underscores when accessing foreignKeys. So Lets Say you rename your class to: TraineeStatus and the id change it to traineeStatusId..
Secondly, You can use the Hibernate annotations for what you need. but first you need to know how the relation is:
#OneToMany : One Trainee can have lots of statuses
#ManytoOne : Many trainees can have the same status
#OneToOne : one Trainee Can only have one status and the other way around.
Try this:
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#OneToOne
private TraineeStatus status;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
...
You can change the #OneToOne for the one you need..
Remember that hibernate will try to map this in your Trainees mysql table as status_traineeStatusId, so if you have this column (as an integer) at your trainess table you are done :)..
That is it..
Hope it helps
My Hibernate-JPA domain model has these entities:
AttributeType ------< AttributeValue
The relevant Java classes look like this (getters and setters omitted):
#Entity
public class AttributeType {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(unique = true, nullable = false)
private String name;
#OneToMany(mappedBy = "attributeType", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private List<AttributeValue> values = new ArrayList<AttributeValue>();
}
#Entity #Table(uniqueConstraints = #UniqueConstraint(columnNames = {"value", "attribute_type_id"}))
public class AttributeValue {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(optional = false)
private AttributeType attributeType;
#Column(nullable = false)
private String value;
}
Notice there's a unique constraint on AttributeValue.value and AttributeValue.attributeType, because for an attribute type (e.g. size) we don't want to allow an attribute value (e.g. small) to occur more than once.
If I update an AttributeType by performing the following operations within a single transaction:
delete "small" attribute value from "size" attribute type
add "small" attribute value to "size" attribute type
I get an exception that indicates the unique constraint was violated. This suggests that Hibernate-JPA is performing the insertion of the attribute value before the delete, which seems to invite this kind of problem for no obvious reason.
The class that performs the update of an AttributeType looks like this:
#Transactional(propagation = Propagation.SUPPORTS)
public class SomeService {
private EntityManager entityManager; // set by dependency injection
#Transactional(propagation = Propagation.REQUIRED)
public AttributeType updateAttributeType(AttributeType attributeType) throws Exception {
attributeType = entityManager.merge(attributeType);
entityManager.flush();
entityManager.refresh(attributeType);
return attributeType;
}
}
I could workaround this problem by iterating over the attribute values, figuring out which ones have been updated/deleted/inserted, and performing them in this order instead:
deletes
updates
inserts
But it seems like the ORM should be able to do this for me. I've read that Oracle provides a "deferConstraints" option that causes constraints to be checked only when a transaction has completed. However, I'm using SQL Server, so this won't help me.
You need to use a composite ID instead of a generated ID.
HHH-2801
The problem arises when a new association entity with a generated ID
is added to the collection. The first step, when merging an entity
containing this collection, is to cascade save the new association
entity. The cascade must occur before other changes to the collection.
Because the unique key for this new association entity is the same as
an entity that is already persisted, a ConstraintViolationException is
thrown. This is expected behavior.
Using a new collection (i.e., one-shot delete), as suggested in the
previous comment) also results in a constraint violation, since the
new association entity will be saved on the cascade of the new
collection.
An example of one of the approaches (using a composite ID instead of a generated ID) is illustrated >in manytomanywithassocclass.tar.gz and is checked into Svn.
#Entity
public class AttributeType {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
#Column(unique = true, nullable = false)
private String name;
#OneToMany(mappedBy = "attributeType", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private List<AttributeValue> values = new ArrayList<AttributeValue>();
//Getter, Setter...
}
#Entity
#Table (uniqueConstraints = #UniqueConstraint(columnNames = { "value", "attributeType_id" }))
public class AttributeValue{
#EmbeddedId AttributeValueId id;
#MapsId(value= "id")
#ManyToOne(optional = false)
private AttributeType attributeType;
private String value2;
public AttributeValue() {
this.id = new AttributeValueId();
}
public AttributeType getAttributeType() {
return attributeType;
}
public void setAttributeType(AttributeType pAttributeType) {
this.id.setAttributeTypeID(pAttributeType.getId());
this.attributeType = pAttributeType;
}
public String getValue() {
return id.getAttributeValue();
}
public void setValue(String value) {
this.id.setAttributeValue(value);
}
#Embeddable
public static class AttributeValueId implements Serializable {
private Integer id;
private String value;
public AttributeValueId() {
}
public AttributeValueId(Integer pAttributeTypeID, String pAttributeValue) {
this.id = pAttributeTypeID;
this.value = pAttributeValue;
}
public Integer getAttributeTypeID() {
return id;
}
public void setAttributeTypeID(Integer attributeTypeID) {
this.id = attributeTypeID;
}
public String getAttributeValue() {
return value;
}
public void setAttributeValue(String attributeValue) {
this.value = attributeValue;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime
* result
+ ((id == null) ? 0 : id
.hashCode());
result = prime
* result
+ ((value == null) ? 0 : value.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AttributeValueId other = (AttributeValueId) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
}
See 5.1.2.1. Composite identifier on how to do it with JPA annotation.
See Chapter 8. Component Mapping
See 8.4. Components as composite identifiers
I am not sure if I understand the question as it is getting late, but first thing I would try would be to override AttributeValue's equals method to contain those two unique fields.
In hibernate session there is one queue for the delete and one for the insert. Debug to see if deletes comes before insert.
Look at the merge. Try using update instead.