diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/BaseEntity.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/BaseEntity.java new file mode 100644 index 000000000000..936cfdf21036 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/BaseEntity.java @@ -0,0 +1,28 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import java.util.Date; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@MappedSuperclass +public class BaseEntity { + @GeneratedValue( + strategy = GenerationType.IDENTITY + ) + @Id + private Long id; + + @Temporal(TemporalType.TIMESTAMP) + private Date createdOn; + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/Customer.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/Customer.java new file mode 100644 index 000000000000..d18f50dfe148 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/Customer.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToOne; +import javax.persistence.Version; + +@Entity(name = "Customer") +public class Customer extends BaseEntity { + + @Version + @Column(name = "version") + private int version; + + @OneToOne(optional = false, fetch = FetchType.LAZY, cascade = { + CascadeType.PERSIST, + CascadeType.MERGE, + CascadeType.REMOVE + }) + private User user; + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/EntityWithMutableAttributesTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/EntityWithMutableAttributesTest.java new file mode 100644 index 000000000000..138d9cd3796d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/EntityWithMutableAttributesTest.java @@ -0,0 +1,240 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.validation.constraints.NotNull; + +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(BytecodeEnhancerRunner.class) +@CustomEnhancementContext({ NoDirtyCheckEnhancementContext.class, DirtyCheckEnhancementContext.class }) +public class EntityWithMutableAttributesTest extends BaseNonConfigCoreFunctionalTestCase { + + boolean skipTest; + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "100" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + String byteCodeProvider = Environment.getProperties().getProperty( AvailableSettings.BYTECODE_PROVIDER ); + if ( byteCodeProvider != null && !Environment.BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( byteCodeProvider ) ) { + // skip the test if the bytecode provider is Javassist + skipTest = true; + } + else { + sources.addAnnotatedClass( User.class ); + sources.addAnnotatedClass( Role.class ); + } + } + + @Before + public void setUp() { + inTransaction( + session -> { + User user = new User(); + user.setId( 1 ); + user.setDate( new Date() ); + user.setEmail( "not null string" ); + + + Role role = new Role(); + role.setId( 2 ); + role.setDate( new Date() ); + role.setName( "manager" ); + + user.setRole( role ); + + session.save( role ); + session.save( user ); + } + ); + } + + @After + public void tearDown() { + inTransaction( + session -> { + session.createQuery( "delete from User" ).executeUpdate(); + session.createQuery( "delete from Role" ).executeUpdate(); + } + ); + } + + @Test + public void testLoad() { + inTransaction( + session -> { + User user = session.load( User.class, 1 ); + assertThat( + user, instanceOf( PersistentAttributeInterceptable.class ) + ); + final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) user; + assertThat( + interceptable.$$_hibernate_getInterceptor(), + instanceOf( EnhancementAsProxyLazinessInterceptor.class ) + ); + } + ); + } + + @Test + public void testMutableAttributeIsUpdated() { + inTransaction( + session -> { + User user = session.load( User.class, 1 ); + user.getDate().setTime( 0 ); + } + ); + + inTransaction( + session -> { + User user = session.getReference( User.class, 1 ); + assertThat( user.getDate().getTime(), is( 0L ) ); + } + ); + } + + + @Entity(name = "User") + @Table(name = "appuser") + public static class User { + @Id + private Integer id; + + @NotNull + private String email; + + private String name; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "t_date") + private Date date; + + @ManyToOne + private Role role; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + } + + @Entity(name = "Role") + @Table(name = "approle") + public static class Role { + @Id + private Integer id; + + @NotNull + private String name; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "t_date") + private Date date; + + private String description; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpateEntitiesWithCollectionsTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpateEntitiesWithCollectionsTest.java new file mode 100644 index 000000000000..2d28ef1c2974 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/LoadAndUpateEntitiesWithCollectionsTest.java @@ -0,0 +1,251 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import java.util.HashSet; +import java.util.List; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.SessionImplementor; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@TestForIssue(jiraKey = "HHH14424") +@RunWith(BytecodeEnhancerRunner.class) +@CustomEnhancementContext({ DirtyCheckEnhancementContext.class, NoDirtyCheckEnhancementContext.class }) +public class LoadAndUpdateEntitiesWithCollectionsTest extends BaseNonConfigCoreFunctionalTestCase { + + boolean skipTest; + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "100" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + String byteCodeProvider = Environment.getProperties().getProperty( AvailableSettings.BYTECODE_PROVIDER ); + if ( byteCodeProvider != null && !Environment.BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( byteCodeProvider ) ) { + // skip the test if the bytecode provider is Javassist + skipTest = true; + } + else { + sources.addAnnotatedClass( SamplingOrder.class ); + sources.addAnnotatedClass( Customer.class ); + sources.addAnnotatedClass( User.class ); + sources.addAnnotatedClass( Role.class ); + } + } + + @Before + public void setUp() { + inTransaction( + session -> { + User user = new User(); + user.setEmail( "foo@bar.com" ); + + Role role = new Role(); + role.setName( "admin" ); + + user.addRole( role ); + + Customer customer = new Customer(); + customer.setUser( user ); + + SamplingOrder order = new SamplingOrder(); + order.setNote( "it is a sample" ); + order.setCustomer( customer ); + + + session.save( user ); + session.save( role ); + session.save( customer ); + session.save( order ); + } + ); + } + + @After + public void tearDwon() { + inTransaction( + session -> { + session.createQuery( "delete from SamplingOrder" ).executeUpdate(); + session.createQuery( "delete from Customer" ).executeUpdate(); + session.createQuery( "delete from User" ).executeUpdate(); + session.createQuery( "delete from Role" ).executeUpdate(); + } + ); + } + + @Test + public void testLoad() { + inTransaction( + session -> { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( SamplingOrder.class ); + Root root = cq.from( SamplingOrder.class ); + root.fetch( SamplingOrder_.customer ); + + TypedQuery query = session.createQuery( cq ); + query.getResultList(); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + } + ); + } + + @Test + public void testAddUserRoles() { + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrder( session ); + User user = samplingOrder.getCustomer().getUser(); + Role role = new Role(); + role.setName( "superuser" ); + user.addRole( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 2 ) ); + } + ); + + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrder( session ); + User user = samplingOrder.getCustomer().getUser(); + Role role = new Role(); + role.setName( "user" ); + user.getRoles().add( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 3 ) ); + } + ); + + inTransaction( + session -> { + User user = session + .createQuery( + "from User", + User.class + ) + .list() + .get( 0 ); + Role role = new Role(); + user.getRoles().add( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + List users = session + .createQuery( + "from User u", + User.class + ) + .list(); + User user = users + .get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 4 ) ); + } + ); + + } + + @Test + public void testDeleteUserRoles() { + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrder( session ); + User user = samplingOrder.getCustomer().getUser(); + user.setRoles( new HashSet<>() ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 0 ) ); + } + ); + } + + @Test + public void testModifyUserMail() { + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrder( session ); + User user = samplingOrder.getCustomer().getUser(); + user.setEmail( "bar@foo.com" ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "bar@foo.com" ) ); + assertThat( user.getRoles().size(), is( 1 ) ); + } + ); + } + + private SamplingOrder getSamplingOrder(SessionImplementor session) { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( SamplingOrder.class ); + Root root = cq.from( SamplingOrder.class ); + root.fetch( SamplingOrder_.customer ); + + TypedQuery query = session.createQuery( cq ); + List resultList = query.getResultList(); + return resultList.get( 0 ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/Role.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/Role.java new file mode 100644 index 000000000000..08c55e964c72 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/Role.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity(name = "Role") +@Table(name = "approle") +public class Role { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", updatable = false, nullable = false) + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/SamplingOrder.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/SamplingOrder.java new file mode 100644 index 000000000000..855d1fde9ad7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/SamplingOrder.java @@ -0,0 +1,52 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity(name = "SamplingOrder") +public class SamplingOrder { + + @Id + @GeneratedValue + private Long id; + + private String note; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "customerId") + private Customer customer; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/SimpleDynamicUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/SimpleDynamicUpdateTest.java new file mode 100644 index 000000000000..bdf0eab5fa0a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/SimpleDynamicUpdateTest.java @@ -0,0 +1,308 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; + +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +@RunWith(BytecodeEnhancerRunner.class) +@CustomEnhancementContext({ NoDirtyCheckEnhancementContext.class, DirtyCheckEnhancementContext.class }) +public class SimpleDynamicUpdateTest extends BaseNonConfigCoreFunctionalTestCase { + + boolean skipTest; + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "100" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + String byteCodeProvider = Environment.getProperties().getProperty( AvailableSettings.BYTECODE_PROVIDER ); + if ( byteCodeProvider != null && !Environment.BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( byteCodeProvider ) ) { + // skip the test if the bytecode provider is Javassist + skipTest = true; + } + else { + sources.addAnnotatedClass( User.class ); + sources.addAnnotatedClass( Role.class ); + } + } + + @Before + public void setUp() { + inTransaction( + session -> { + User user = new User(); + user.setId( 1 ); + user.setEmail( "not null string" ); + + Address address = new Address(); + address.setState( "Texas" ); + + user.setAddress( address ); + + Role role = new Role(); + role.setId( 2 ); + role.setName( "manager" ); + + user.setRole( role ); + + session.save( role ); + session.save( user ); + } + ); + } + + @Test + public void testIt() { + inTransaction( + session -> { + User user = session.getReference( User.class, 1 ); + assertThat( + user, instanceOf( PersistentAttributeInterceptable.class ) + ); + final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) user; + assertThat( + interceptable.$$_hibernate_getInterceptor(), + instanceOf( EnhancementAsProxyLazinessInterceptor.class ) + ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + entity.setName( "abc" ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + assertThat( entity.getName(), is( "abc" ) ); + assertThat( entity.getEmail(), is( "not null string" ) ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + entity.setRole( null ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + assertThat( entity.getName(), is( "abc" ) ); + assertThat( entity.getEmail(), is( "not null string" ) ); + assertThat( entity.getRole(), is( nullValue() ) ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + entity.setName( null ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + assertThat( entity.getName(), is( nullValue() ) ); + assertThat( entity.getEmail(), is( "not null string" ) ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + entity.setAddress( null ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + assertThat( entity.getName(), is( nullValue() ) ); + assertThat( entity.getEmail(), is( "not null string" ) ); + assertThat( entity.getRole(), is( nullValue() ) ); + assertThat( entity.getAddress(), is( nullValue() ) ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + Role role = new Role(); + role.setId( 3 ); + role.setName( "user" ); + entity.setRole( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + User entity = session.getReference( User.class, 1 ); + assertThat( entity.getName(), is( nullValue() ) ); + assertThat( entity.getEmail(), is( "not null string" ) ); + assertThat( entity.getRole(), is( notNullValue() ) ); + assertThat( entity.getAddress(), is( nullValue() ) ); + } + ); + } + + @Entity(name = "User") + @Table(name = "appuser") + @DynamicUpdate + public static class User { + @Id + private Integer id; + + @NotNull + private String email; + + private String name; + + private Address address; + + @ManyToOne + private Role role; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + } + + @Entity(name = "Role") + @Table(name = "approle") + @DynamicUpdate + public static class Role { + @Id + private Integer id; + + @NotNull + private String name; + + private String description; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + } + + @Embeddable + public static class Address { + private String street; + private String state; + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + } + +} + diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/User.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/User.java new file mode 100644 index 000000000000..30fb4c2612d6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/User.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Entity(name = "User") +@Table(name = "appuser") +public class User extends BaseEntity { + + @Column(unique = true, nullable = false) + @NotNull + private String email; + + private String name; + + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable(name = "user_in_role", joinColumns = @JoinColumn(name = "userid"), inverseJoinColumns = @JoinColumn(name = "roleid")) + public Set roles = new HashSet<>(); + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public void addRole(Role role) { + this.roles.add( role ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/BaseEntity.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/BaseEntity.java new file mode 100644 index 000000000000..a51df3112660 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/BaseEntity.java @@ -0,0 +1,28 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.dynamicupdate; + +import java.util.Date; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@MappedSuperclass +public class BaseEntity { + @GeneratedValue( + strategy = GenerationType.IDENTITY + ) + @Id + private Long id; + + @Temporal(TemporalType.TIMESTAMP) + private Date createdOn; + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/Customer.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/Customer.java new file mode 100644 index 000000000000..d4cc22671095 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/Customer.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.dynamicupdate; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.OneToOne; +import javax.persistence.Version; + +import org.hibernate.annotations.DynamicUpdate; + +@Entity(name = "Customer") +@DynamicUpdate +public class Customer extends BaseEntity { + + @Version + @Column(name = "version") + private int version; + + @OneToOne(optional = false, fetch = FetchType.LAZY, cascade = { + CascadeType.PERSIST, + CascadeType.MERGE, + CascadeType.REMOVE + }) + private User user; + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java new file mode 100644 index 000000000000..24616b88340d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/DynamicUpdateAndCollectionsTest.java @@ -0,0 +1,258 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.dynamicupdate; + +import java.util.HashSet; +import java.util.List; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.SessionImplementor; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext; +import org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.NoDirtyCheckEnhancementContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +@TestForIssue(jiraKey = "HHH14424") +@RunWith(BytecodeEnhancerRunner.class) +@CustomEnhancementContext({ NoDirtyCheckEnhancementContext.class, DirtyCheckEnhancementContext.class }) +public class DynamicUpdateAndCollectionsTest extends BaseNonConfigCoreFunctionalTestCase { + + boolean skipTest; + + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "100" ); + ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + String byteCodeProvider = Environment.getProperties().getProperty( AvailableSettings.BYTECODE_PROVIDER ); + if ( byteCodeProvider != null && !Environment.BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( byteCodeProvider ) ) { + // skip the test if the bytecode provider is Javassist + skipTest = true; + } + else { + sources.addAnnotatedClass( SamplingOrder.class ); + sources.addAnnotatedClass( Customer.class ); + sources.addAnnotatedClass( User.class ); + sources.addAnnotatedClass( Role.class ); + } + } + + @Before + public void setUp() { + inTransaction( + session -> { + User user = new User(); + user.setEmail( "foo@bar.com" ); + + Role role = new Role(); + role.setName( "admin" ); + + user.addRole( role ); + + Customer customer = new Customer(); + customer.setUser( user ); + + SamplingOrder order = new SamplingOrder(); + order.setNote( "it is a sample" ); + order.setCustomer( customer ); + + session.save( user ); + session.save( role ); + session.save( customer ); + session.save( order ); + } + ); + } + + @After + public void tearDwon() { + inTransaction( + session -> { + session.createQuery( "delete from SamplingOrder" ).executeUpdate(); + session.createQuery( "delete from Customer" ).executeUpdate(); + session.createQuery( "delete from User" ).executeUpdate(); + session.createQuery( "delete from Role" ).executeUpdate(); + } + ); + } + + @Test + public void testLoad() { + inTransaction( + session -> { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( SamplingOrder.class ); + Root root = cq.from( SamplingOrder.class ); + root.fetch( SamplingOrder_.customer ); + + TypedQuery query = session.createQuery( cq ); + query.getResultList(); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + } + ); + } + + @Test + public void testRemoveCustomers() { + Long samplingOrderId = fromTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrderFetchCustomer( session ); + samplingOrder.setCustomer( null ); + return samplingOrder.getId(); + } + ); + + inTransaction( + session -> { + SamplingOrder samplingOrder = session.get( SamplingOrder.class, samplingOrderId ); + assertThat( samplingOrder.getCustomer(), is( nullValue() ) ); + } + ); + } + + @Test + public void testAddUserRoles() { + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrderFetchCustomer( session ); + User user = samplingOrder.getCustomer().getUser(); + Role role = new Role(); + role.setName( "superuser" ); + user.addRole( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 2 ) ); + } + ); + + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrderFetchCustomer( session ); + User user = samplingOrder.getCustomer().getUser(); + Role role = new Role(); + user.getRoles().add( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 3 ) ); + } + ); + + inTransaction( + session -> { + User user = session.createQuery( "from User", User.class ).list().get( 0 ); + Role role = new Role(); + user.getRoles().add( role ); + session.save( role ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "foo@bar.com" ) ); + assertThat( user.getRoles().size(), is( 4 ) ); + } + ); + + } + + @Test + public void testDeleteUserRoles() { + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrderFetchCustomer( session ); + User user = samplingOrder.getCustomer().getUser(); + user.setRoles( new HashSet<>() ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getRoles().size(), is( 0 ) ); + } + ); + } + + @Test + public void testModifyUserMail() { + inTransaction( + session -> { + SamplingOrder samplingOrder = getSamplingOrderFetchCustomer( session ); + User user = samplingOrder.getCustomer().getUser(); + user.setEmail( "bar@foo.com" ); + } + ); + + inTransaction( + session -> { + List users = session.createQuery( "from User u", User.class ).list(); + User user = users.get( 0 ); + assertThat( user.getEmail(), is( "bar@foo.com" ) ); + assertThat( user.getRoles().size(), is( 1 ) ); + } + ); + } + + private SamplingOrder getSamplingOrderFetchCustomer(SessionImplementor session) { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery( SamplingOrder.class ); + Root root = cq.from( SamplingOrder.class ); + root.fetch( SamplingOrder_.customer ); + + TypedQuery query = session.createQuery( cq ); + List resultList = query.getResultList(); + return resultList.get( 0 ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/Role.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/Role.java new file mode 100644 index 000000000000..cd8c685ddeea --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/Role.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.dynamicupdate; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.DynamicUpdate; + +@Entity(name = "Role") +@Table(name = "approle") +@DynamicUpdate +public class Role { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", updatable = false, nullable = false) + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/SamplingOrder.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/SamplingOrder.java new file mode 100644 index 000000000000..c25711d34391 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/SamplingOrder.java @@ -0,0 +1,60 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.dynamicupdate; + +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.hibernate.annotations.DynamicUpdate; + +@Entity(name = "SamplingOrder") +@DynamicUpdate +public class SamplingOrder { + + @Id + @GeneratedValue + private Long id; + + private String note; + + @OneToMany + private List customers; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "customerId") + private Customer customer; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/User.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/User.java new file mode 100644 index 000000000000..c8d8eb62d764 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/inlinedirtychecking/dynamicupdate/User.java @@ -0,0 +1,65 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.dynamicupdate; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.annotations.DynamicUpdate; + +@Entity(name = "User") +@Table(name = "appuser") +@DynamicUpdate +public class User extends BaseEntity { + + @Column( unique = true, nullable = false) + @NotNull + private String email; + + private String name; + + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable(name = "user_in_role", joinColumns = @JoinColumn(name = "userid"), inverseJoinColumns = @JoinColumn(name = "roleid")) + public Set roles = new HashSet<>(); + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } + + public void addRole(Role role) { + this.roles.add( role ); + } +}