Hibernate Delete Cascade - java

I Have one entity [Project] that contains a collection of other entities [Questions].
I have mapped the relation with a cascade attribute of "all-delete-orphan".
In my DB the relation is mapped with a project_id (FK) field on the questions table. this field cannot be null since I don't want a Question without a Project.
When I do session.delete(project) it throws an exception saying that project_id cant be null, but if I remove the not-null constraint to that field, the deletion works nice.
Anyone knows how to solve this?

Straight from the documentation. This explains your problem exactly i believe:
However, this code
Parent p = (Parent) session.Load(typeof(Parent), pid);
// Get one child out of the set
IEnumerator childEnumerator = p.Children.GetEnumerator();
childEnumerator.MoveNext();
Child c = (Child) childEnumerator.Current;
p.Children.Remove(c);
c.Parent = null;
session.Flush();
will not remove c from the database; it will only remove the link to p (and cause a NOT NULL constraint violation, in this case). You need to explicitly Delete() the Child.
Parent p = (Parent) session.Load(typeof(Parent), pid);
// Get one child out of the set
IEnumerator childEnumerator = p.Children.GetEnumerator();
childEnumerator.MoveNext();
Child c = (Child) childEnumerator.Current;
p.Children.Remove(c);
session.Delete(c);
session.Flush();
Now, in our case, a Child can't really exist without its parent. So if we remove a Child from the collection, we really do want it to be deleted. For this, we must use cascade="all-delete-orphan".
<set name="Children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Edit:
With regards to the inverse stuff, i believe this only determines how the sql is generated, see this doc for more info.
One thing to note is, have you got
not-null="true"
on the many-to-one relationship in your hibernate config?

One strategy is to mark the foreign-key in the database with on-delete-cascade, so as soon as NHibernate tells the database to delete a project, the database itself will cascade the deletes. Then you have to tell NHibernate that the database itself does a cascade delete.

If anyone have situation like this then make sure you actually have set proper CascadeType (one of: ALL, REMOVE, DELETE). It needs to be in the entity that you try to delete:
public class Project {
#Id
private long id;
#OneToMany(mappedBy = "project", cascade = {CascadeType.REMOVE})
public List<Question> questions;
}
Deleting should work whether there is NOT NULL constraint on foreign key or not just with:
session.delete(project);

The delete is occurring on the Project first and cascading to the Question, but the Project delete includes a nulling of the project_id in the Questions (for referential integrity. You're not getting an exception on the deletion of the Question object, but because the cascade is trying to null the FK in the Question(s).
Looking at "Java Persistence with Hibernate", I think that what you really want a cascade type of delete or remove, not delete-orphans.

Related

Is it possible to temporarily disable cascading for a Hibernate entity?

Given a Hibernate/JPA entity with cascading set to ALL for a related entity:
#Entity
public class Entity {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "entity")
private Set<RelatedEntities> relatedEntities;
}
Is it possible to temporarily turn off the cascading, e.g. to allow Entity to be persisted without also persisting its relatedEntities?
No, it is not possible to do it, and at least according to my modest opinion, it would not be a good thing to do so either. When other developers look at the mappings and the code that does persist/merge/delete... they would expect the cascades to be applied and introduce the unexpected behavior if they oversee that the cascades are temporarily disabled somewhere else for the code they are about to change.
However, you can map to the same table a new entity class which does not have the fields that are cascaded. Then just use that entity in situations in which you don't want the cascades to be applied.
You can't temporarily disable cascading (to my knowledge, at least), but since you use Hibernate you can insert new entity using HQL
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
There is always a "manual" solution where you remember relatedEntities in a variable for later use, and set null value as its value on Entity instance before persisting it.

Hibernate JPA: #OneToMany delete old, insert new without flush

I actually never quite understood this behavior in hibernate.
I am using a #OneToMany relationship in a Entity called 'Parent', which is annotated like this:
#OneToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true)
#JoinColumn(name = "entity_id", insertable = true, updatable = true, nullable = false)
private List<Child> children;
Now I want to do the following within one transaction:
Get the parent entity
iterate through the list of children
delete one of the children
insert a new child
So, basically I am just entirely replacing one of the children.
As far as I understand this problem, I should be able to do something like this:
(please note that this is just some java pseudocode to illustrate the problem)
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteAndAdd(Long parentId, Long childId) {
Parent parent = entityManager.find(parentId);
for (Iterator it = parent.children.iterator(); it.hasNext();) {
Child child = it.next();
if (child.id == childId) {
it.remove();
}
}
Child newChild = new Child();
parent.children.add(newChild);
}
However, this fails in case the new Child has the same unique key values as the old one. So, basically it seems like the old child entity isn't removed properly, before the new one is persisted.
If I add a entityManager.flush() between deleting the old child and persisting the new child like this:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteAndAdd(Long parentId, Long childId) {
Parent parent = entityManager.find(parentId);
for (Iterator it = parent.children.iterator(); it.hasNext();) {
Child child = it.next();
if (child.id == childId) {
it.remove();
}
}
entityManager.flush();
Child newChild = new Child();
parent.children.add(newChild);
}
Everything works fine. The child is deleted before the new one is inserted, as it should.
As I don't want to asume that hibernate mixes up the order of the statements that are sent to the DB, there must be something else I am assuming about hibernate which isn't the case. Any ideas why the latter example works, while the first one doesn't?
Hibernate version is 3.5.
DB is Mysql InnoDB
Hibernate doesn't know about, nor respect, all database constraints (e.g. MySQL unique constraints). It's a known issue they don't plan on addressing anytime soon.
Hibernate has a defined order for the way operations occur during a flush.
Entity deletions will always happen after inserts. The only answers I know about are to remove the constraint or add the additional flush.
EDIT: By the way, the reason for the defined order is that this is the only way to guarantee foreign key constraints (one of the constraints they DO care about) aren't violated, even if the user does something out of order.
For the sake of future readers, one way to resolve this issue is to use deferred constraints. PostgreSQL and Oracle support them, maybe other RDBMS' too. Hibernate will issue all statements within a transaction, and deferral will ensure that constraints are enforced upon transaction commit only. In PostgreSQL, for example:
ALTER TABLE company
ADD CONSTRAINT name_unique UNIQUE (name) DEFERRABLE INITIALLY DEFERRED;
It is not ideal, but it is simple and effective.

Does cascade="all-delete-orphan" have any meaning in a Hibernate unidirectional many-to-many association with a join table?

I have two objects which form a parent-child relationship which have a many-to-many relationship. Following the recommendations in the Hibernate reference manual, I have mapped this using a join table:
<class name="Conference" table="conferences">
...
<set name="speakers" table="conference_speakers" cascade="all">
<key column="conference_id"/>
<many-to-many class="Speaker" column="speaker_id"/>
</set>
</class>
<class name="Speaker" table="speakers">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="firstName"/>
<property name="lastName"/>
</class>
My wish is that a single Speaker can be associated with many different Conferences, but also that any Speaker which is no longer referenced by any Conference is removed from the speakers table (as a Speaker with no associated conferences doesn't have much meaning in my project).
However, I've found that if I use cascade="all-delete-orphan", then if a Speaker which is associated with multiple Conferences is removed from just one of them, Hibernate attempts to delete the Speaker instance itself.
Below is a unit test which shows this behavior:
#Test
public void testRemoveSharedSpeaker() {
int initialCount = countRowsInTable("speakers");
Conference c1 = new Conference("c1");
Conference c2 = new Conference("c2");
Speaker s = new Speaker("John", "Doe");
c1.getSpeakers().add(s);
c2.getSpeakers().add(s);
conferenceDao.saveOrUpdate(c1);
conferenceDao.saveOrUpdate(c2);
flushHibernate();
assertEquals(initialCount + 1, countRowsInTable("speakers"));
assertEquals(2, countRowsInTable("conference_speakers"));
// the remove:
c1 = conferenceDao.get(c1.getId());
c1.getSpeakers().remove(s);
flushHibernate();
assertEquals("count should stay the same", initialCount + 1, countRowsInTable("speakers"));
assertEquals(1, countRowsInTable("conference_speakers"));
c1 = conferenceDao.get(c1.getId());
c2 = conferenceDao.get(c2.getId());
assertEquals(0, c1.getSpeakers().size());
assertEquals(1, c2.getSpeakers().size());
}
An error is thrown when s's removal from c1.speakers is processed, because Hibernate is deleting both the row in the join table and the speakers table row as well:
DEBUG org.hibernate.SQL - delete from conference_speakers where conference_id=? and speaker_id=?
DEBUG org.hibernate.SQL - delete from speakers where id=?
If I change cascade="all-delete-orphan" to just cascade="all", then this test works as expected, although it leads to the undesired behavior where I will end up with orphaned rows in my speakers table.
This leads me to wonder - is it even possible for Hibernate to know when to delete orphaned objects from the child-side of the relationship, but only when the child is not referenced by any other parents (whether or not those parents are in the current Session)? Perhaps I am misusing cascade="all-delete-orphan"?
I get the same exact behavior if I use JPA annotations instead of XML mapping such as:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "conference_speakers",
joinColumns = #JoinColumn(name = "conference_id"),
inverseJoinColumns = #JoinColumn(name = "speaker_id"))
#org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Speaker> speakers = new HashSet<Speaker>();
This is with Hibernate 3.6.7.Final, by the way.
DELETE_ORPHAN cascade mode is not defined for many-to-many relationship - only for one-to-many (the latter sports a "orphanRemoval=true|false" attribute within JPA standard #OneToMany annotation, so you don't have to resort to proprietary Hibernate annotation).
The reason for this is exactly as you've described - there's no way for Hibernate to figure out whether "orphaned" end of the many-to-many relationship is truly orphaned without running a query against the database which is both counter-intuitive and can (potentially) have serious performance implications.
Hibernate behavior you've described is therefore correct (well, "as documented"); though in a perfect world it would have alerted you to the fact that DELETE_ORPHAN is illegal on many-to-many during 2nd pass mappings compilation.
I can't think of a good way of achieving what you want to do, to be honest. The easiest (but database-specific) way would likely be to define a trigger on deletion from conference_speakers that would check whether this speaker is "truly" orphaned and delete it from speakers if so. The database-independent option is to do the same thing manually in DAO or listener.
Update: Here's an excerpt from Hibernate docs (Chapter 11.11, right after gray Note on CascadeType.ALL), highlights are mine:
A special cascade style, delete-orphan, applies only to one-to-many
associations, and indicates that the delete() operation should be
applied to any child object that is removed from the association.
Further down:
It does not usually make sense to enable cascade on a many-to-one or
many-to-many association. In fact the #ManyToOne and #ManyToMany don't
even offer a orphanRemoval attribute. Cascading is often useful for
one-to-one and one-to-many associations.

Deleting an element from one-to-many collection (Java + HIbernate + Struts)

I can't delete a child object from the database. From the org.apache.struts.action.Action.execute() method, I am removing the child from the parent's List, and also calling session.delete(child). I've simplified the code below and only included what I believe to be relavent.
Hibernate Mapping
<class
name="xxx.xxx.hibernate.Parent"
table="parent">
...
<list
name="children"
cascade="all,delete-orphan"
lazy="true"
inverse="true">
<key column="parent_id"/>
<index column="list_index"/>
<one-to-many class="xxx.xxx.hibernate.Child"/>
</list>
</class>
<class
name="xxx.xxx.hibernate.Child"
table="child">
...
<many-to-one
name="parent"
class="xxx.xxx.hibernate.Parent"
not-null="true"
column="parent_id" />
</class>
Excerpt from execute() method
Transaction tx = session.beginTransaction(); //session is of type org.hibernate.Session
try {
Parent parent = (Parent) session.get(Parent.class, getParentId());
Iterator i = form.getDeleteItems().iterator(); //form is of type org.apache.struts.action.ActionForm
while(i.hasNext()){
Child child = (Child) i.next();
session.delete(child);
parent.getChildren().remove(child); //getChildren() returns type java.util.List
}
session.saveOrUpdate(parent);
tx.commit();
} ...
I've tried with only session.delete(child); and I've tried with only parent.getChildren().remove(child); and with both lines, all without success. There are no errors or thrown exceptions or anything of the sort. I'm sure this code gets called (I've even used System.out.println(); to trace what's happening), but the database isn't updated. I can add children using similar code, edit non-collection properties of existing children, edit the parent's properties, all of that works, just not deleting!
According to the Hibernate FAQ I'm doing the mapping right, and according to this SO question I've got the right logic. I've looked all over the internet and can't seem to find anything else.
What am I doing wrong? Please help! Thanks.
Notes on versions
Everything is a few years old:
Java 1.4.2
SQL Server 2005
Hibernate 3.0.5
Struts 1.2.7
Apache Tomcat 5.0.28
If you haven't overridden the equals() method, the entity is probably not found in the list, because it has been detached, and is now a different instance. That's why the remove isn't working. Then even if the delete works, the objects are re-cascacde because they still exist in the collection. Here's what to do:
either override the equals() (and hashCode()) method(s), using either the id (easy) or some sort of busines key (more appropriate) (search stackoverflow for tips for overrideing these two metods), and leave only getChildren().remove(child)
Iterate over the collection of children in the first loop, like this:
Iterator<Child> i = form.getDeleteItems().iterator();
while(i.hasNext()){
Child child = i.next();
for (Iterator<Child> it = parent.getChildren().iterator();) {
if (child.getId().equals(it.next().getId()) {
it.remove(); // this removes the child from the underlying collection
}
}
}
I'm not sure what causes this behavior in hibernate, you can get going by loading the Child first. Separately deleting the Child is not nessesary. Updated code should look like;
Transaction tx = session.beginTransaction(); //session is of type org.hibernate.Session
try {
Parent parent = (Parent) session.get(Parent.class, getParentId());
Iterator i = form.getDeleteItems().iterator(); //form is of type org.apache.struts.action.ActionForm
while(i.hasNext()){
Child child = (Child) session.get(Chile.class, ((Child) i.next()).getChildId());
parent.getChildren().remove(child); //getChildren() returns type java.util.List
}
session.saveOrUpdate(parent);
tx.commit();
} ...
show the SQL generated by Hibernate
<property name="show_sql">true</property>
<property name="format_sql">true</property>
Edit:
Check out this Chapter 10. Working with objects
In this case, the Child class is the owner of the inverse relation, Hibernate will look at the parent reference of the child to determine whether the relation is still there. Since you don't set the parent to null, the relation exists and the child may not be deleted. Try doing
parent.getChildren().remove(child);
child.parent = null;
session.delete(child);
Also remove the not-null="true" from the parent property mapping.
The best thing to do when working with inverse associations, is to update both sides in Java code, that way you can continue working with the objects in memory and you don't have to worry about which side owns the relation.
A similar situation is discussed here: http://simoes.org/docs/hibernate-2.1/155.html

How can I make a JPA OneToOne relation lazy

In this application we are developing, we noticed that a view was particularly slow. I profiled the view and noticed that there was one query executed by hibernate which took 10 seconds even if there only were two object in the database to fetch. All OneToMany and ManyToMany relations were lazy so that wasn't the problem. When inspecting the actual SQL being executed, I noticed that there were over 80 joins in the query.
Further inspecting the issue, I noticed that the problem was caused by the deep hierarchy of OneToOne and ManyToOne relations between entity classes. So, I thought, I'll just make them fetched lazy, that should solve the problem. But annotating either #OneToOne(fetch=FetchType.LAZY) or #ManyToOne(fetch=FetchType.LAZY) doesn't seem to work. Either I get an exception or then they are not actually replaced with a proxy object and thus being lazy.
Any ideas how I'll get this to work? Note that I do not use the persistence.xml to define relations or configuration details, everything is done in java code.
First off, some clarifications to KLE's answer:
Unconstrained (nullable) one-to-one association is the only one that can not be proxied without bytecode instrumentation. The reason for this is that owner entity MUST know whether association property should contain a proxy object or NULL and it can't determine that by looking at its base table's columns due to one-to-one normally being mapped via shared PK, so it has to be eagerly fetched anyway making proxy pointless. Here's a more detailed explanation.
many-to-one associations (and one-to-many, obviously) do not suffer from this issue. Owner entity can easily check its own FK (and in case of one-to-many, empty collection proxy is created initially and populated on demand), so the association can be lazy.
Replacing one-to-one with one-to-many is pretty much never a good idea. You can replace it with unique many-to-one but there are other (possibly better) options.
Rob H. has a valid point, however you may not be able to implement it depending on your model (e.g. if your one-to-one association is nullable).
Now, as far as original question goes:
A) #ManyToOne(fetch=FetchType.LAZY) should work just fine. Are you sure it's not being overwritten in the query itself? It's possible to specify join fetch in HQL and / or explicitly set fetch mode via Criteria API which would take precedence over class annotation. If that's not the case and you're still having problems, please post your classes, query and resulting SQL for more to-the-point conversation.
B) #OneToOne is trickier. If it's definitely not nullable, go with Rob H.'s suggestion and specify it as such:
#OneToOne(optional = false, fetch = FetchType.LAZY)
Otherwise, if you can change your database (add a foreign key column to owner table), do so and map it as "joined":
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="other_entity_fk")
public OtherEntity getOther()
and in OtherEntity:
#OneToOne(mappedBy = "other")
public OwnerEntity getOwner()
If you can't do that (and can't live with eager fetching) bytecode instrumentation is your only option. I have to agree with CPerkins, however - if you have 80!!! joins due to eager OneToOne associations, you've got bigger problems then this :-)
To get lazy loading working on nullable one-to-one mappings you need to let hibernate do compile time instrumentation and add a #LazyToOne(value = LazyToOneOption.NO_PROXY) to the one-to-one relation.
Example Mapping:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="other_entity_fk")
#LazyToOne(value = LazyToOneOption.NO_PROXY)
public OtherEntity getOther()
Example Ant Build file extension (for doing the Hibernate compile time instrumentation):
<property name="src" value="/your/src/directory"/><!-- path of the source files -->
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries -->
<property name="destination" value="/your/build/directory"/><!-- path of your build directory -->
<fileset id="applibs" dir="${libs}">
<include name="hibernate3.jar" />
<!-- include any other libraries you'll need here -->
</fileset>
<target name="compile">
<javac srcdir="${src}" destdir="${destination}" debug="yes">
<classpath>
<fileset refid="applibs"/>
</classpath>
</javac>
</target>
<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath>
<fileset refid="applibs"/>
</classpath>
</taskdef>
<instrument verbose="true">
<fileset dir="${destination}">
<!-- substitute the package where you keep your domain objs -->
<include name="/com/mycompany/domainobjects/*.class"/>
</fileset>
</instrument>
</target>
Unless you are using Bytecode Enhancement, you cannot fetch lazily the parent-side #OneToOne association.
However, most often, you don't even need the parent-side association if you use #MapsId on the child-side:
#Entity(name = "PostDetails")
#Table(name = "post_details")
public class PostDetails {
#Id
private Long id;
#Column(name = "created_on")
private Date createdOn;
#Column(name = "created_by")
private String createdBy;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
}
With #MapsId, the id property in the child table serves as both Primary Key and Foreign Key to the parent table Primary Key.
So, if you have a reference to the parent Post entity, you can easily fetch the child entity using the parent entity identifier:
PostDetails details = entityManager.find(
PostDetails.class,
post.getId()
);
This way, you won't have N+1 query issues that could be caused by the mappedBy #OneToOne association on the parent side.
Here's something that has been working for me (without instrumentation):
Instead of using #OneToOne on both sides, I use #OneToMany in the inverse part of the relationship (the one with mappedBy). That makes the property a collection (List in the example below), but I translate it into an item in the getter, making it transparent to the clients.
This setup works lazily, that is, the selects are only made when getPrevious() or getNext() are called - and only one select for each call.
The table structure:
CREATE TABLE `TB_ISSUE` (
`ID` INT(9) NOT NULL AUTO_INCREMENT,
`NAME` VARCHAR(255) NULL,
`PREVIOUS` DECIMAL(9,2) NULL
CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);
The class:
#Entity
#Table(name = "TB_ISSUE")
public class Issue {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
#Column
private String name;
#OneToOne(fetch=FetchType.LAZY) // one to one, as expected
#JoinColumn(name="previous")
private Issue previous;
// use #OneToMany instead of #OneToOne to "fake" the lazy loading
#OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
// notice the type isnt Issue, but a collection (that will have 0 or 1 items)
private List<Issue> next;
public Integer getId() { return id; }
public String getName() { return name; }
public Issue getPrevious() { return previous; }
// in the getter, transform the collection into an Issue for the clients
public Issue getNext() { return next.isEmpty() ? null : next.get(0); }
}
The basic idea behing the XToOnes in Hibernate is that they are not lazy in most case.
One reason is that, when Hibernate have to decide to put a proxy (with the id) or a null,
it has to look into the other table anyway to join. The cost of accessing the other table in the database is significant, so it might as well fetch the data for that table at that moment (non-lazy behaviour), instead of fetching that in a later request that would require a second access to the same table.
Edited: for details, please refer to ChssPly76 's answer. This one is less accurate and detailed, it has nothing to offer. Thanks ChssPly76.
In native Hibernate XML mappings, you can accomplish this by declaring a one-to-one mapping with the constrained attribute set to true. I am not sure what the Hibernate/JPA annotation equivalent of that is, and a quick search of the doc provided no answer, but hopefully that gives you a lead to go on.
As already perfectly explained by ChssPly76, Hibernate's proxies don't help with unconstrained (nullable) one-to-one associations, BUT there is a trick explained here to avoid to set up instrumentation. The idea is to fool Hibernate that the entity class which we want to use has been already instrumented: you instrument it manually in the source code. It's easy! I've implemented it with CGLib as bytecode provider and it works (ensure that you configure lazy="no-proxy" and fetch="select", not "join", in your HBM).
I think this is a good alternative to real (I mean automatic) instrumentation when you have just one one-to-one nullable relation that you want to make lazy. The main drawback is that the solution depends on the bytecode provider you are using, so comment your class accurately because you could have to change the bytecode provider in the future; of course, you are also modifying your model bean for a technical reason and this is not fine.
This question is quite old, but with Hibernate 5.1.10, there are some new better comfortable solution.
Lazy loading works except for the parent side of a #OneToOne association. This is because Hibernate has no other way of knowing whether to assign a null or a Proxy to this variable. More details you can find in this article
You can activate lazy loading bytecode enhancement
Or, you can just remove the parent side and use the client side with #MapsId as explained in the article above. This way, you will find that you don’t really need the parent side since the child shares the same id with the parent so you can easily fetch the child by knowing the parent id
.
For Kotlin devs: To allow Hibernate to inherit from the #Entity types that you want to be lazy-loadable they have to be inheritable/open, which they in Kotlin by default are not. To work around this issue we can make use of the all-open compiler plugin and instruct it to also handle the JPA annotations by adding this to our build.gradle:
allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
If you are using Kotlin and Spring like me, you are most probably also using the kotlin-jpa/no-args and kotlin-spring/all-open compiler plugins already. However, you will still need to add the above lines, as that combination of plugins neither makes such classes open.
Read the great article of Léo Millon for further explanations.
Most efficient mapping of a one-to-one association
You can avoid all these problems and get rid of the foreign key column by using the same primary key value for both associated entities. You can do that by annotating the owning side of the association with #MapsId.
#Entity
public class Book {
#Id
#GeneratedValue
private Long id;
#OneToOne(mappedBy = "book", fetch = FetchType.LAZY, optional = false)
private Manuscript manuscript;
...
}
#Entity
public class Manuscript {
#Id
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "id")
private Book book;
...
}
Book b = em.find(Book.class, 100L);
Manuscript m = em.find(Manuscript.class, b.getId());
More Detail click on this url
If the relation must not be bidirectional then an #ElementCollection might be easier than using a lazy One2Many collection.
If the child entity is used readonly, then it's possible to simply lie and set optional=false.
Then ensure that every use of that mapped entity is preloaded via queries.
public class App {
...
#OneToOne(mappedBy = "app", fetch = FetchType.LAZY, optional = false)
private Attributes additional;
and
String sql = " ... FROM App a LEFT JOIN FETCH a.additional aa ...";
... maybe even persisting would work...

Categories

Resources