HHH-10111 - AttributeConverter based attributes are not marked for update when their state is modified
(cherry picked from commit bb0b604b57
)
This commit is contained in:
parent
1ae495b7c9
commit
4a19270612
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.type.descriptor.converter;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||
|
||||
/**
|
||||
* For now we need to treat attributes to which a converter has been applied as mutable.
|
||||
* See Jira HHH-10111 for details.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AttributeConverterMutabilityPlanImpl<T> extends MutableMutabilityPlan<T> {
|
||||
private final AttributeConverter attributeConverter;
|
||||
|
||||
public AttributeConverterMutabilityPlanImpl(AttributeConverter attributeConverter) {
|
||||
this.attributeConverter = attributeConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T deepCopyNotNull(T value) {
|
||||
return (T) attributeConverter.convertToEntityAttribute( attributeConverter.convertToDatabaseColumn( value ) );
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import javax.persistence.AttributeConverter;
|
|||
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -31,6 +32,8 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
|||
private final Class jdbcType;
|
||||
private final AttributeConverter<? extends T,?> attributeConverter;
|
||||
|
||||
private final AttributeConverterMutabilityPlanImpl<T> mutabilityPlan;
|
||||
|
||||
public AttributeConverterTypeAdapter(
|
||||
String name,
|
||||
String description,
|
||||
|
@ -46,6 +49,8 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
|||
this.jdbcType = jdbcType;
|
||||
this.attributeConverter = attributeConverter;
|
||||
|
||||
this.mutabilityPlan = new AttributeConverterMutabilityPlanImpl<T>( attributeConverter );
|
||||
|
||||
log.debug( "Created AttributeConverterTypeAdapter -> " + name );
|
||||
}
|
||||
|
||||
|
@ -66,6 +71,11 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
|||
return attributeConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MutabilityPlan<T> getMutabilityPlan() {
|
||||
return mutabilityPlan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.type.converter;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DirtyCheckingTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
public void dirtyCheckAgainstNewNameInstance() {
|
||||
SomeEntity simpleEntity = new SomeEntity();
|
||||
simpleEntity.setId( 1L );
|
||||
simpleEntity.setName( new Name( "Steven" ) );
|
||||
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.save( simpleEntity );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
session.getTransaction().begin();
|
||||
SomeEntity loaded = session.byId( SomeEntity.class ).load( 1L );
|
||||
loaded.setName( new Name( "Steve" ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
session.getTransaction().begin();
|
||||
loaded = session.byId( SomeEntity.class ).load( 1L );
|
||||
assertEquals( "Steve", loaded.getName().getText() );
|
||||
session.delete( loaded );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dirtyCheckAgainstMutatedNameInstance() {
|
||||
SomeEntity simpleEntity = new SomeEntity();
|
||||
simpleEntity.setId( 1L );
|
||||
simpleEntity.setName( new Name( "Steven" ) );
|
||||
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.save( simpleEntity );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
session.getTransaction().begin();
|
||||
SomeEntity loaded = session.byId( SomeEntity.class ).load( 1L );
|
||||
loaded.getName().setText( "Steve" );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
session.getTransaction().begin();
|
||||
loaded = session.byId( SomeEntity.class ).load( 1L );
|
||||
assertEquals( "Steve", loaded.getName().getText() );
|
||||
session.delete( loaded );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dirtyCheckAgainstNewNumberInstance() {
|
||||
// numbers (and most other java types) are actually immutable...
|
||||
SomeEntity simpleEntity = new SomeEntity();
|
||||
simpleEntity.setId( 1L );
|
||||
simpleEntity.setNumber( 1 );
|
||||
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.save( simpleEntity );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
session.getTransaction().begin();
|
||||
SomeEntity loaded = session.byId( SomeEntity.class ).load( 1L );
|
||||
loaded.setNumber( 2 );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
session = openSession();
|
||||
session.getTransaction().begin();
|
||||
loaded = session.byId( SomeEntity.class ).load( 1L );
|
||||
assertEquals( 2, loaded.getNumber().intValue() );
|
||||
session.delete( loaded );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {SomeEntity.class};
|
||||
}
|
||||
|
||||
public static class Name {
|
||||
private String text;
|
||||
|
||||
public Name() {
|
||||
}
|
||||
|
||||
public Name(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NameConverter implements AttributeConverter<Name, String> {
|
||||
public String convertToDatabaseColumn(Name name) {
|
||||
return name == null ? null : name.getText();
|
||||
}
|
||||
|
||||
public Name convertToEntityAttribute(String s) {
|
||||
return s == null ? null : new Name( s );
|
||||
}
|
||||
}
|
||||
|
||||
public static class IntegerConverter implements AttributeConverter<Integer, String> {
|
||||
public String convertToDatabaseColumn(Integer value) {
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
public Integer convertToEntityAttribute(String s) {
|
||||
return s == null ? null : Integer.parseInt( s );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "SomeEntity")
|
||||
public static class SomeEntity {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Convert(converter = IntegerConverter.class)
|
||||
@Column(name = "num")
|
||||
private Integer number;
|
||||
|
||||
@Convert(converter = NameConverter.class)
|
||||
@Column(name = "name")
|
||||
private Name name = new Name();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(Integer number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public Name getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(Name name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue