HHH-18195 migration guide
This commit is contained in:
parent
fea3b2f061
commit
856a001170
|
@ -105,8 +105,7 @@ public class BagDelayedOperationTest {
|
||||||
Parent p = session.get( Parent.class, parentId );
|
Parent p = session.get( Parent.class, parentId );
|
||||||
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
// add detached Child c
|
// add detached Child c
|
||||||
session.lock( c1, LockOptions.NONE );
|
p.addChild( session.merge( c1 ) );
|
||||||
p.addChild( c1 );
|
|
||||||
// collection should still be uninitialized
|
// collection should still be uninitialized
|
||||||
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,12 @@ package org.hibernate.orm.test.map;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.dialect.MariaDBDialect;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ public class MapIndexFormulaTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SkipForDialect( dialectClass = MariaDBDialect.class, reason = "HHH-18433")
|
||||||
public void testIndexFormulaMap(SessionFactoryScope scope) {
|
public void testIndexFormulaMap(SessionFactoryScope scope) {
|
||||||
User turin = new User( "turin", "tiger" );
|
User turin = new User( "turin", "tiger" );
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
|
@ -90,8 +92,7 @@ public class MapIndexFormulaTest {
|
||||||
assertEquals( 1, g.getUsers().size() );
|
assertEquals( 1, g.getUsers().size() );
|
||||||
Map smap = ( (User) g.getUsers().get( "gavin" ) ).getSession();
|
Map smap = ( (User) g.getUsers().get( "gavin" ) ).getSession();
|
||||||
assertEquals( 1, smap.size() );
|
assertEquals( 1, smap.size() );
|
||||||
session.lock( turin , LockMode.NONE);
|
User gavin = (User) g.getUsers().put( "gavin", session.merge( turin ) );
|
||||||
User gavin = (User) g.getUsers().put( "gavin", turin );
|
|
||||||
session.remove( gavin );
|
session.remove( gavin );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
0l,
|
0l,
|
||||||
|
|
|
@ -153,56 +153,6 @@ String isDefault();
|
||||||
* Removed `org.hibernate.annotations.CascadeType.DELETE` in favor of `org.hibernate.annotations.CascadeType#REMOVE`
|
* Removed `org.hibernate.annotations.CascadeType.DELETE` in favor of `org.hibernate.annotations.CascadeType#REMOVE`
|
||||||
* Removed the attribute value from `@DynamicInsert` and `@DynamicUpdate`
|
* Removed the attribute value from `@DynamicInsert` and `@DynamicUpdate`
|
||||||
|
|
||||||
[WARNING]
|
|
||||||
===
|
|
||||||
The removal of `CascadeType.SAVE_UPDATE` slightly changes the persist and flush behaviour (not affecting application using `Entitymanager`) that now conforms with the Jakarta JPA specifications.
|
|
||||||
|
|
||||||
Persisting a transient entity with an associated detached entity where the association is annotated with cascade=all or cascade=persist throws an exception if the detached entity has not been re-associated with the the session using lock or merge.
|
|
||||||
|
|
||||||
The same happens when flushing a managed entity having an associated detached entity.
|
|
||||||
|
|
||||||
```
|
|
||||||
@Entit
|
|
||||||
class Parent {
|
|
||||||
...
|
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
|
||||||
@LazyCollection(value = LazyCollectionOption.EXTRA)
|
|
||||||
private Set<Child> children = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
class Child {
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
private Parent parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
// Being Child c1 detached.
|
|
||||||
|
|
||||||
scope.inTransaction(
|
|
||||||
session -> {
|
|
||||||
Parent parent = session.get( Parent.class, parentId );
|
|
||||||
// add detached Child c
|
|
||||||
parent.addChild( c1 );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
will throw an `jakarta.persistence.EntityExistsException`
|
|
||||||
|
|
||||||
in order to fix the issue we can call `session.lock(c1,LockMode.NONE)` before adding `c1` to the `parent` or instead using `p.addChild( session.merge(c1) )`;
|
|
||||||
|
|
||||||
|
|
||||||
[[ddl-implicit-datatype-timestamp]]
|
[[ddl-implicit-datatype-timestamp]]
|
||||||
== Default precision for timestamp on some databases
|
== Default precision for timestamp on some databases
|
||||||
|
|
||||||
|
@ -244,6 +194,61 @@ one file at a time. This is now done across the entire set of `hbm.xml` files a
|
||||||
While most users will never see this change, it might impact integrations which tie-in to
|
While most users will never see this change, it might impact integrations which tie-in to
|
||||||
XML processing.
|
XML processing.
|
||||||
|
|
||||||
|
[[flush-persist]]
|
||||||
|
== Session flush and persist
|
||||||
|
|
||||||
|
The removal of `CascadeType.SAVE_UPDATE` slightly changes the persist and flush behaviour (not affecting application using `Entitymanager`) that now conforms with the Jakarta JPA specifications.
|
||||||
|
|
||||||
|
Persisting a transient entity or flushing a manged entity with an associated detached entity having the association annotated with `cascade = CascadeType.ALL` or `cascade = CascadeType.PERSIST` throws now an `jakarta.persistence.EntityExistsException` if the detached entity has not been re-associated with the the Session.
|
||||||
|
|
||||||
|
To re-associate the detached entity with the Session the `Session#merge` method can be used.
|
||||||
|
|
||||||
|
Consider the following model
|
||||||
|
|
||||||
|
```
|
||||||
|
@Entity
|
||||||
|
class Parent {
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
||||||
|
@LazyCollection(value = LazyCollectionOption.EXTRA)
|
||||||
|
private Set<Child> children = new HashSet<>();
|
||||||
|
|
||||||
|
public void addChild(Child child) {
|
||||||
|
children.add( child );
|
||||||
|
child.setParent( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
class Child {
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Parent parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming we have c1 as a detached Child, the following code will now result in jakarta.persistence.EntityExistsException being thrown at flush time:
|
||||||
|
|
||||||
|
```
|
||||||
|
Parent parent = session.get( Parent.class, parentId );
|
||||||
|
parent.addChild( c1 );
|
||||||
|
```
|
||||||
|
Instead, c1 must first be re-associated with the Session using merge:
|
||||||
|
|
||||||
|
```
|
||||||
|
Parent parent = session.get( Parent.class, parentId );
|
||||||
|
Child merged = session.merge( c1 );
|
||||||
|
parent.addChild( merged );
|
||||||
|
```
|
||||||
|
|
||||||
[[todo]]
|
[[todo]]
|
||||||
== Todos (dev)
|
== Todos (dev)
|
||||||
|
|
Loading…
Reference in New Issue