I have property entity and floor plan entities like this
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "properties", indexes = {#Index(name = "properties_parent_id_index", columnList = "parent_id", unique = true)})
public class PropertyEntity extends BaseEntity {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "property_id")
private List<FloorPlanEntity> floorPlans;
....
...
}
now I try to delete children and save parent
like this `
propertyEntity.getFloorPlans().clear();
propertyDataService.save(propertyEntity);
but it gives exceptions like this
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "property_id" of relation "floor_plans" violates not-null constraint
I think you can use the #OneToMany(orphanRemoval = true)
It provides a way to delete orphaned entities from the database.
So, it should remove the FloorPlanEntity from database when it's removed from the list.
There is some tutorial with explanation:
https://www.baeldung.com/jpa-cascade-remove-vs-orphanremoval#orphanremoval
Related
My database contains a table with a composite-primary-key such that one of the keys is a foreign key, and the other is supposed to be used to get an entity from an external service. The code looks somewhat like this:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Embeddable
#EqualsAndHashCode
public class PrimaryKey implements Serializable {
#Column(name = "A_ID")
private Long aId;
#Column(name = "F_ID")
private Long fId;
}
#Entity
#Table(name = "JOIN_ENTITY")
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class JoinEntity {
#EmbeddedId
private PrimaryKey pk;
#MapsId("aId")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "A_ID")
private EntityA a;
public getFId() { return pk.getFId(); }
}
#Entity
#Table(name = "ENTITY_A")
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class EntityA implements Serializable {
....
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true)
List<JoinEntity> list = new ArrayList<>();
}
When I get a JoinEntity saved and try to get EntityA from the database the list is not being populated, but if I get some JoinEntity from the database the related EntityA is recovered correctly. What do I do to get the JoinEntity list to be recovered with the EntityA?
You need to use FetchType.EAGER on the #OneToMany association in the EntityA class:
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
List<JoinEntity> list = new ArrayList<>();
This way when you retrieve a EntityA from the database, its JoinEntitys will be automatically retrieved.
Solved the problem adding an ENTITY_F table with the id and switching to a simple ManyToMany relationship.
I am getting the above error when trying to run a custom query. I understand that with hibernate, you need to map to the entity names (not the column names). However, in the case of a #OneToMany, I don't have the column in the child. Let me explain with a simple example (I've removed all other columns and methods):
#Query("SELECT ch.randomColumnHere FROM Parent pa INNER JOIN Child ch ON pa.id = ch.parent_id")
Parent.class
#Entity(name="Parent")
#Table(name="parent")
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PACKAGE)
#NoArgsConstructor
#Setter
#Getter
public class Parent {
#Id
#SequenceGenerator(name = "parent_id_seq", sequenceName = "parent_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "parent_id_seq")
private Long id;
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "parent_id", nullable = false, updatable = false)
private List<Child> children;
}
Child.class
#Entity(name="Child")
#Table(name="child")
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PACKAGE)
#NoArgsConstructor
#Setter
#Getter
public class Child {
#Id
#SequenceGenerator(name = "child_id_seq", sequenceName = "child_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "child_id_seq")
private Long id;
#Column(name="randomcolumnhere")
private Double randomColumnHere;
}
I get the following exception:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: could not resolve property: parent_id
I understand that there is no field called parent_id in the Child entity. How can I get around this? Since the only 'reference' to the parent_id column is the #JoinColumn of the Parent class.
Any suggestions?
Add relationship in child class as follow and change query accordingly.
#ManyToOne
private Parent p;
#Query("SELECT ch.randomColumnHere FROM Parent pa INNER JOIN Child ch ON pa.id = ch.p.id")
HQL queries use entities and their associations. The fact that the association uses a join table or not is not important for HQL: you navigate through associations and Hibernate does the appropriate translation to SQL.
SELECT ch.randomColumnHere FROM Parent pa INNER JOIN pa.children;
I have the following "audit" table that can host audit information about any other class of my schema:
CREATE TABLE `audit` (
`table_name` varchar(45) NOT NULL,
`item_id` int(11) NOT NULL,
`version` int(11) NOT NULL,
`updated_at` datetime NOT NULL,
`updated_by` varchar(25) NOT NULL,
`comment` varchar(255) DEFAULT NULL,
PRIMARY KEY (`table_name`,`item_id`,`version`)
)
Then, I have different JPA entities in my schema like so:
#Entity(name = "EntityA")
#Table(name = "entity_a")
public class EntityA {
#Id
#GeneratedValue
private Long id;
private Long version;
// Other fields
#OneToOne(mappedBy = "id.item", targetEntity = EntityAAudit.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private EntityAAudit audit;
}
At the same time, I have an asbtract class Audit that is a super-class for multiple entity-specific Audit classes:
#MappedSuperclass
#Table(name = "audit")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "table_name", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(insert = true, force = true)
public abstract class AuditHistory {
// Some audit fields like the date and the author of the modification
}
#Entity(name = "EntityAAudit")
#Table(name = "audit")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("entity_a")
#DiscriminatorOptions(insert = true, force = true)
public class EntityAAudit extends Audit {
#EmbeddedId
#JsonUnwrapped
private AuditedId id;
#Embeddable
public static class AuditedId implements Serializable {
#OneToOne
#JoinColumn(name = "item_id", nullable = false)
private EntityA item;
#Column(name = "version", nullable = false)
private Long version;
}
}
This mapping works when retrieving the entities and their audit information from the database, but not when inserting a new entity with the corresponding audit information:
EntityA entity = new EntityA();
entity.setVersion(/* ... */);
// Setting the other basic fields of the entity
EntityAAudit audit = new EntityAAudit();
// Setting the basic fields of the audit
entity.setAudit(audit);
entity = em.merge(entity);
I end up with the following exception:
org.hibernate.id.IdentifierGenerationException: null id generated for:class EntityAAudit
I have literally tried everything I could think of or find online, and in the end it always comes down to the same issue: Hibernate tries to insert my Audit object with empty values for the item_id and the version.
If I manually set my entity instance and the version as the id of the audit object like so:
EntityA entity = new EntityA();
entity.setVersion(/* ... */);
// Setting the other basic fields of the entity
EntityAAudit audit = new EntityAAudit();
// Setting the basic fields of the audit
audit.setId(new EntityAAudit.AuditedId());
audit.getId().setItem(entity);
audit.getId().setVersion(entity.getVersion());
entity.setAudit(audit);
entity = em.merge(entity);
Then I end up with the even more obscure error here:
Caused by: java.lang.NullPointerException
at org.hibernate.type.descriptor.java.AbstractTypeDescriptor.extractHashCode(AbstractTypeDescriptor.java:65)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:185)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:189)
at org.hibernate.type.EntityType.getHashCode(EntityType.java:348)
Note that I cannot change the structure of my database nor the version of Hibernate (5.1.0, I know some bugs are fixed in later versions that could solve my issue...).
Thanks a lot :)
You might try a "derived identity" mapping:
#Entity(name = "EntityAAudit")
#Table(name = "audit")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("entity_a")
#DiscriminatorOptions(insert = true, force = true)
public class EntityAAudit extends Audit {
#EmbeddedId
#JsonUnwrapped
private AuditedId id;
#OneToOne
#JoinColumn(name = "item_id", nullable = false)
#MapsId("entityAId") // maps entityAId attribute of embedded id
private EntityA item;
#Embeddable
public static class AuditedId implements Serializable {
private Long entityAId; // corresponds to PK type of EntityA
#Column(name = "version", nullable = false)
private Long version;
}
}
Note the #MapsId annotation on EntityAAudit.item.
Also, you will need to explicitly set EntityAAudit.item and AuditedId.version. JPA does not magically determine and set any circular references for you.
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I have the following entity:
#Entity
#Getter
#Setter
#ToString
public class Team extends EntityBase {
......
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
orphanRemoval = true, optional = false)
#JoinColumn(name = "auth_id")
private TeamAuthentication auth;
......
}
so it has a TeamAuthentication table reference. The latter entity looks as follows:
#Entity
#Getter
#Setter
#ToString
public class TeamAuthentication {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String accessToken;
}
What I want is that when I fetch existing Team entity from the table and replace a reference to TeamAuthentication table there for field auth, then persist this Team entity with teamRepository.save(), I want that old TeamAuthentication would be deleted from its table. At the moment it stays in the table and becomes sort of a loitering entry that won't be ever used or queried.
How can I leverage Hibernate cascade in deleting OneToOne reference on change?
I am trying to set a ManyToMany annotation on my code:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "user")
public class User extends AbstractUser
{
#ManyToMany(mappedBy = "promotors", cascade = CascadeType.PERSIST)
#JoinTable(name = "user_student",
joinColumns=#JoinColumn(name="promotor_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="student_id", referencedColumnName="id")
)
private Collection<User> students;
#ManyToMany
private Collection<User> promotors;
}
However every time i try to run the application and the db gets generated, it creates 2 tables for the ManyToMany, 1 new table that is called user_student as i defined beneath, but it also creates a second table user_user which i didn't define but is generated from the promotors.
It's correct you cannot map many to many relationship on one table. As you have only one possible column to map it to. What enforces one to one relationship.
You always have to have mapping table. Its also most convenient way to map many to many relationships on different tables.
Apparently, i didn't define the second ManyToMany correctly, this is the correct code:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "user")
public class User extends AbstractUser
{
#ManyToMany
#JoinTable(name = "user_student",
joinColumns={#JoinColumn(name="promotor_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="student_id", referencedColumnName="id")}
)
private Collection<User> students;
#ManyToMany(mappedBy = "students", cascade = CascadeType.PERSIST)
private Collection<User> promotors;
}