jpa one to one relationship issue when persist - java

I have 2 models with one to one relationship
#Entity
#Table(name = "Form_Item_Production")
public class FormItemProduction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id", nullable = false)
private Long itemId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
getter and setter
}
The other one
#Entity
#Table(name = "App_Lookup")
public class AppLookup {
#Id
#GeneratedValue
#Column(name = "Lookup_Id", nullable = false)
private Long lookupId;
#Column(name = "Lookup_Name", length = 30, nullable = false)
private String lookupName;
getter and setter
}
When I try to persist to save the formitemproduction values
public boolean insertItem(List<FormItemProduction> f) {
for (FormItemProduction i : f) {
System.out.println("A" + i.getAppLookup().getLookupId()); // prints the correct id of applookup
i.setItemId(null);
entityManager.persist(i);
}
entityManager.flush();
return true;
}
I get this error
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.mamee.factory.security.entity.AppLookup
From my understanding this is unidirectional one to one mapping so I don't quite understand why I'm getting the error detached?

You have itemId which can't be null.
#Column(name = "item_id", nullable = false)
private Long itemId;
and you actually set itemId to null
i.setItemId(null);
So this line
entityManager.persist(i);
Is not able to persist your data.

Fixed by changing from:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
to:
#OneToOne
#JoinColumn(name = "shift_lookup_id", insertable = true, updatable = true)
private AppLookup appLookup;

You have set cascade to ALL:
#OneToOne(cascade = CascadeType.ALL)
Which means you are cascading all operations down to the related field AppLookup.
You can set cascade to none, and you will no longer get your error, but no db operations will be executed for AppLookup field.

Related

#ManyToOne relationship is not audited

I have two entities:
#Entity
#Table(name = "entity_a")
#Audited
public class EntityA {
#Column(name = "entity_a_uuid", columnDefinition = "char", updatable = false)
#Type(type = "uuid-char")
private UUID uuid = UUID.randomUUID();
/**
* #deprecated in favor of uuid
*/
#Deprecated
#Id
#GeneratedValue
#Column(name = "entity_a_id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "entity_a_id", nullable = false)
#BatchSize(size = 100)
#NotAudited
private List<EntityB> entityBs = new ArrayList<>();
}
and
#Entity
#Audited
#Table(name = "entity_b")
public class EntityB {
#Id
#Column(name = "entity_b_uuid", columnDefinition = "char", updatable = false)
#Type(type = "uuid-char")
private UUID uuid = UUID.randomUUID();
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "entity_a_id", nullable = false, insertable = false, updatable = false)
private EntityA entityA;
}
Each is correctly audited into two tables entity_a_audit and entity_b_audit. However, the entity_a_id field in entity_b_audit is always null.
Some details:
If I do not have the #NotAudited in EntityA, I will get an error that says something to the effect of: The table EntityA_EntityB_audit does not exist. This seems like it's trying to audit them as a single table, which I do not want.
I have tried applying #Audited(targetAuditMode = elationTargetAuditMode.NOT_AUDITED) to each side. If applied only in EntityA, I get the above error. If applied only in EntityB, nothing changes. If applied in both, I get the error above. If applied to neither, I get the error above.
I suspect the entity_a_id is null in entity_b_audit because the id isn't generated until EntityA hits the DB. entity_a_id is auto-incrementing in the entity_a table.
Using hibernate-envers-5.4.32.Final.jar
Ultimately, I would like for entity_a_id to not be null in entity_b_audit. Alternatively, if I could somehow get entity_a_uuid to be captured instead, that would also suffice.
Any help would be greatly appreciated! Thanks.
You marked the column as insertable = false, updatable = false, so there is nothing to audit here, because Hibernate can never change the value of that column.

#ManyToOne in self-referencing entity with composite key - #IdClass, java, hibernate

Spent 3 days looking for a solution and finally I came here for community wisdom.
I have self-referencing entity like follows:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#IdClass(CompositeUserId.class)
#Table(name = "user", schema = "dbo")
public class User implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#Id
#Column(name = "first_name")
private String firstName;
#Id
#Column(name = "last_name")
private String lastName;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
#JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
private User parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
private Set<User> children;
my CompositeUserId.class:
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class UserCompositeId implements Serializable {
private Integer id;
private String firstName;
private String lastName;
When I try retrieve all data from my user table I get error:
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ...User with id UserCompositeId#19e66569; nested exception is javax.persistence.EntityNotFoundException:
I suppose there might be some kind of mistake in the #JoinColumn block.
Here is the sql query causing the error:
SELECT *
FROM dbo.user ur1 LEFT OUTER JOIN dbo.user ur2 ON ur1.first_name=ur2.first_name AND ur1.parent_id=ur2.id AND ur1.last_name=ur2.last_name
WHERE ur1.first_name='First Name' AND ur1.id=130 AND ur1.last_name='Last Name'
I ensured that the request does not return anything in the database by running it manually, but found out that if I will change id to parent_id it will return data, so again, probably some mistake in #JoinColumn block
You need you use #NotFound(action=NotFoundAction.IGNORE). If there is no records then it will assign null to it.
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
#JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
#NotFound(action=NotFoundAction.IGNORE)
private User parent;
The foreign key columns need different names than the primary key columns, e.g. like this (notice the 'parent' prefix in name):
#JoinColumn(name = "parent_id", referencedColumnName = "id", updatable = false, insertable = true)
#JoinColumn(name = "parent_firstname", referencedColumnName = "firstname", updatable = false, insertable = true)
#JoinColumn(name = "parent_lastname", referencedColumnName = "lastname", updatable = false, insertable = true)
#ManyToOne(fetch = FetchType.EAGER)
private UserEntity parent;
Additionally, the insertable value should be true, since otherwise no associations are persisted during the insert.
Be aware that a parent needs to be saved before a child can be associated. Since the parent fk is not updatable, is must be set on a newly created child before it is saved.
Feel free to have a look at this project containing the complete sample code:
Entity:
https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/main/java/org/demo/UserEntity.java
Integration-Test:
https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/test/java/org/demo/DemoApplicationTests.java
Also it is good practice to synchronize bidirectional associations:
https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
The sample entity could e.g. use some methods like these:
public void addChild(UserEntity child) {
child.parent = this; // sync owning side of the association
this.children.add(child);
}
public void setParent(UserEntity parent) {
parent.addChild(this);
}

Hibernate unexpected deletes when finding entities

We have a Java ee application running on JBoss 6.4 GA using JPA and Hibernate with the following entities:
#Entity
#SequenceGenerator(name = "sequence", sequenceName="SEQ_CAMPAIGNS_ID",allocationSize = 1)
#Table(name = "CAMPAIGN")
public class CampaignEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "IS_ACTIVE", nullable = false)
private boolean active;
#Column(name = "START_DATE", nullable = false)
private Date startDate;
#Column(name = "END_DATE", nullable = false)
private Date endDate;
#Column(name = "LEGAL_ENTITY_ID", nullable = false)
private Integer legalEntityId;
#Column(name = "DEPARTMENT", nullable = false)
#Enumerated(value = EnumType.STRING)
private Department department;
#Column(name = "CATEGORY", nullable = false)
#Enumerated(value = EnumType.STRING)
private Category category;
#Embedded
CampaignConditionsEntity campaignConditions;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "campaign", orphanRemoval = true)
#OrderBy
private List<CodeEntity> campaignCodes;
public CampaignEntity() {
}
And the following CampaignConditionsEntity:
#Embeddable
public class CampaignConditionsEntity implements Serializable {
private static final String CAMPAIGN_ID = "CAMPAIGN_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAMPAIGN_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CAMPAIGN_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
And the following CodeEntity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#SequenceGenerator(name = "sequence", sequenceName = "SEQ_CODES_ID", allocationSize = 1)
public abstract class CodeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAMPAIGN_ID")
private CampaignEntity campaign;
#OneToOne(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private DiscountEntity discount;
#Column(name = "MAX_USAGES", nullable = false)
private Integer maxUsages;
#Column(name = "UNLIMITED_USAGES", nullable = false)
private boolean unlimitedUsages;
#Column(name = "NEGATIVE_SH", nullable = false)
private boolean negativeSH;
#Column(name = "UNIQUE_BUYER", nullable = false)
private boolean uniqueBuyer;
#Column(name = "START_DATE")
private Date startDate;
#Column(name = "END_DATE")
private Date endDate;
#Embedded
private CodeConditionsEntity codeConditions;
public CodeEntity() {
}
This is the CodeConditionsEntity:
#Embeddable
public class CodeConditionsEntity implements Serializable {
private static final String CODE_ID = "CODE_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CODE_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_CARRIERS", joinColumns = #JoinColumn(name = CODE_ID))
private Set<CarrierConditionEntity> carrierConditions;
This is the CarrierConditionEntity:
#Embeddable
public class CarrierConditionEntity implements Serializable {
#Column(name = "CARRIER", nullable = false, length = 3)
private String carrierCode;
#Column(name = "IS_INCLUDED", nullable = false)
private boolean included;
The problem is that in the logs we are finding unexpected deletes when the only operation that we are doing are finds of particular campaign entities.
In the production logs we find the following deletes
Hibernate: delete from CODE_COND_CARRIERS where CODE_ID=? and CARRIER=? and IS_INCLUDED=?
do you have any suggestion?
thanks
I have some suggestions :)
Be aware of what is a Persistence Context (EntityManager instance in JPA terminology / Session in Hibernate one), the entity lifecycle and transaction scope (unit of work)
Do not mutate entity state if you don't expect the changes to be reflected in database, or at least detach the entity before mutating it.
Mark your transaction as "readOnly" if you only fetch data in the related unit of work. (beware that if you have many "Transactional" methods joining the same physical transaction, the flag is set by the surrounding one and cannot be overridden by inner logical transactions). That way the EntityManager won't be flushed at the end of the transaction and pending changes won't be persisted to the database.
You can track the method triggering the unexpected deletion using an EntityListener on the related entity and printing the current strackTrace (new Throwable().printStackTrace()/ log(new Throwable()) in the PreRemove method
I found where was the problem:
The problem was that the Entities didn't have the equals() and the hashcode() implemented. Also there were entities that have a #PostLoad that modified the entity after loading it from database. Then in this situation Hibernate though that there was a change in those entities that didn't have the equals and the hashcode, and then it delete all of them and inserted again in the database (to have the same entities before the query)
Adding the equals and hashcode methods and deleting postload removed the unexpected deletes and inserts from the logs.
regards

Unidirectional OneToMany with Composite Primary key not persisted

I have an ER relationship from a legacy DB (MS SQL Server based as below
The way that I'm currently trying to convert this to the JPA 2.1 style is as below
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Orders implements Serializable {
#Id
#GeneratedValue
#Column(name = "OrderNumber", nullable = false)
private Integer orderNumber;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "OrderHistory",
joinColumns = {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false)
}
)
private List<OrderHistory> orderHistory;
----Other properties, Getters and Setters
}
#Entity
#PrimaryKeyJoinColumn(name = "OrderNumber")
public class SpecialOrders extends Orders implements Serializable {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false)
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#Fetch(FetchMode.SELECT)
#OrderBy("sequenceNumber ASC")
private List<Items> items;
----Other properties, Getters and Setters
}
#Entity
#IdClass(ItemsPK.class)
public class Items implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(
name = "CustomOptions",
joinColumns = {
#JoinColumn(name = "OrderNumber", referencedColumnName = "OrderNumber", nullable = false),
#JoinColumn(name = "SequenceNumber", referencedColumnName = "SequenceNumber", nullable = false)
}
)
private List<CustomOptions> customOptions;
----Other properties, Getters and Setters
}
#Entity
public class ItemsPK implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
----Getters and Setters
}
#Entity
#IdClass(CustomOptionsPK.class)
public class CustomOptions implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#Id
#Column(name = "OptionNumber", nullable = false)
private Integer optionNumber;
----Other properties, Getters and Setters
}
public class CustomOptionsPK implements Serializable {
#Id
#Column(name = "OrderNumber", nullable = false, insertable = false, updatable = false)
private Integer orderNumber;
#Id
#Column(name = "SequenceNumber", nullable = false)
private Integer sequenceNumber;
#Id
#Column(name = "OptionNumber", nullable = false)
private Integer optionNumber;
----Getters and Setters
}
With the above code, I see that hibernate does below
INSERTs into Orders and gets the GeneratedId for OrderNumber
INSERTs into SpecialOrders, using the orderNumber retrieved above.
Attemps to INSERT into Items table, with a NULL value in the orderNumber and then fails because the orderNumber is a NOT NULL column.
Subsequently, If add a "Simple" primary key to Items table and make the orderNumber as a NULLable column, then the below happens:
INSERTs into Orders and gets the GeneratedId for OrderNumber
INSERTs into SpecialOrders, using the orderNumber retrieved above.
INSERTs into Items table with orderNumber as NULL value and gets the generated id of the Items table row.
Updates the row of Items table with the orderNumber from parent, using the retrieved id for Items.
Attemps to INSERT into CustomOptions table, with a NULL value in the orderNumber and then fails because the orderNumber is a NOT NULL column.
As per the above sequence, it seems that:
Composite Primary key doesnt seem to be working correctly or not supported.
Hibernate is handling the OneToMany relationship inefficiently by issuing an INSERT followed by an UPDATE, instead of just an insert.
Any idea if my understanding is correct? The only way of fixing this issue seems to be that I need to remove the composite primary key and replace it with a simple one.

Hibernate JPA IdentifierGenerationException: null id generated for class with #embeddedid

I am having trouble mapping my database domain model to the program entities in one case where the entity is essentially a join table (a period) which combines two other entities (a timeslot and a day). Another entity (a lesson) then has a reference to this period entity, determining when it occurs.
When I try to save a lesson with a new period using saveOrUpdate(lesson) hibernate throws an IdentifierGenerationException
org.hibernate.id.IdentifierGenerationException: null id generated for:class com.trials.domain.Period
The database looks like below (not the real database, just the key tables and columns)
In the java hibernate model, I have used an embedded id for the primary key of the period class and the lesson class then has a reference to a period.
Period.java
#Entity
#Table(name = "period")
public class Period{
#EmbeddedId
private PeriodId periodId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "day_idday", nullable = false, insertable = false, updatable = false)
private Day day;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "timeslot_idtimeslot", nullable = false, insertable = false, updatable = false)
private Timeslot timeslot;
//constructors, getters, setters, hashcode, and equals
}
And the embedded id just has the primary key columns:
PeriodId.java
#Embeddable
public class PeriodId implements Serializable {
#Column(name = "timeslot_idtimeslot")
private int timeslotId;
#Column(name = "day_idday")
private int dayId;
//constructors, getters, setters, hashcode, and equals
}
Then there is the lesson class that uses the period defined as:
Lesson.java
#Entity
#Table(name = "lesson")
public class Lesson {
#Id
#Column(name = "idlesson")
private int lessonId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumns({#JoinColumn(name = "period_timeslot_idtimeslot", nullable = false, updatable = false), #JoinColumn(name = "period_day_idday", nullable = false, updatable = false)})
private Period period;
//constructors, getters, setters, hashcode, and equals
}
The Timeslot and Day entity classes are both very basic pojos, and their ids use GenerationType.AUTO. So my problems are:
What causes this IdentifierGenerationException
How to avoid it while keeping the same database model
Thanks in advance
Put those guys
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "day_idday", nullable = false, insertable = false, updatable = false)
private Day day;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "timeslot_idtimeslot", nullable = false, insertable = false, updatable = false)
private Timeslot timeslot;
inside the PeriodId class and throw away those ints. I have done a mapping similar to yours this way and it works.
I was able to create the following mapping for my case (scala code) and could totally throw away the #Embeddable class:
#Entity
#Table(name = "payment_order_item", schema = "pg")
#IdClass(classOf[PaymentOrderItem])
final class PaymentOrderItem extends Serializable{
#Id
#ManyToOne
#JoinColumn(name = "order_item_id", referencedColumnName = "id")
var orderItem: OrderItem = _
#Id
#ManyToOne
#JoinColumn(name = "payment_id", referencedColumnName = "id")
var payment: Payment = _
}
So the following should work for you then
#Entity
#Table(name = "period")
#IdClass(Period.class)
public class Period extends Serializable{
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "day_idday", referencedColumnName = "id", nullable = false)
private Day day;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "timeslot_idtimeslot", referencedColumnName = "id", nullable = false)
private Timeslot timeslot;
//constructors, getters, setters, hashcode, and equals
}
On a first glance,
You're missing the generated value annotation in the embedded id class.
#Embeddable
public class PeriodId implements Serializable {
#GeneratedValue
#Column(name = "timeslot_idtimeslot")
private int timeslotId;
#GeneratedValue
#Column(name = "day_idday")
private int dayId;
//constructors, getters, setters, hashcode, and equals
}

Categories