HHH-12338 - Incorrect metamodel for basic collections
This commit is contained in:
parent
139f354873
commit
fe619ceb08
|
@ -138,14 +138,20 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
|
|||
accessTypeInfo.setDefaultAccessType( entity.getEntityAccessTypeInfo().getAccessType() );
|
||||
}
|
||||
}
|
||||
if ( collection.equals( "javax.persistence.metamodel.MapAttribute" ) ) {
|
||||
return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity );
|
||||
}
|
||||
else {
|
||||
return new AnnotationMetaCollection(
|
||||
entity, element, collection, getElementType( declaredType, targetEntity )
|
||||
if ( TypeUtils.containsAnnotation( element, Constants.CONVERT ) ||
|
||||
TypeUtils.containsAnnotation( element, Constants.HIBERNATE_TYPE ) ) {
|
||||
return new AnnotationMetaSingleAttribute(
|
||||
entity,
|
||||
element,
|
||||
TypeUtils.toTypeString( declaredType )
|
||||
);
|
||||
}
|
||||
if ( collection.equals( Constants.MAP_ATTRIBUTE ) ) {
|
||||
return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity );
|
||||
}
|
||||
return new AnnotationMetaCollection(
|
||||
entity, element, collection, getElementType( declaredType, targetEntity )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,6 +30,9 @@ public final class Constants {
|
|||
public static final String MAP_KEY_CLASS = "javax.persistence.MapKeyClass";
|
||||
public static final String ELEMENT_COLLECTION = "javax.persistence.ElementCollection";
|
||||
public static final String ACCESS = "javax.persistence.Access";
|
||||
public static final String MAP_ATTRIBUTE = "javax.persistence.metamodel.MapAttribute";
|
||||
public static final String CONVERT = "javax.persistence.Convert";
|
||||
public static final String HIBERNATE_TYPE = "org.hibernate.annotations.Type";
|
||||
|
||||
public static final Map<String, String> COLLECTIONS = new HashMap<String, String>();
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import org.hibernate.jpamodelgen.test.util.CompilationTest;
|
||||
import org.hibernate.jpamodelgen.test.util.TestForIssue;
|
||||
import org.hibernate.jpamodelgen.test.util.WithClasses;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertAttributeTypeInMetaModelFor;
|
||||
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertListAttributeTypeInMetaModelFor;
|
||||
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
|
||||
|
||||
/**
|
||||
* @author helloztt
|
||||
*/
|
||||
public class CollectionAsBasicTypeTest extends CompilationTest {
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12338")
|
||||
@WithClasses({Goods.class, Product.class})
|
||||
public void testConvert() throws ClassNotFoundException, NoSuchFieldException {
|
||||
assertMetamodelClassGeneratedFor(Product.class);
|
||||
assertMetamodelClassGeneratedFor(Goods.class);
|
||||
assertListAttributeTypeInMetaModelFor(
|
||||
Goods.class,
|
||||
"productList",
|
||||
Product.class,
|
||||
"ListAttribute generic type should be Product"
|
||||
);
|
||||
assertAttributeTypeInMetaModelFor(
|
||||
Goods.class,
|
||||
"tags",
|
||||
Goods.class.getDeclaredField("tags").getGenericType(),
|
||||
"Wrong meta model type"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12338")
|
||||
@WithClasses({Person.class})
|
||||
public void testListType() throws ClassNotFoundException, NoSuchFieldException {
|
||||
assertMetamodelClassGeneratedFor(Person.class);
|
||||
|
||||
assertAttributeTypeInMetaModelFor(
|
||||
Person.class,
|
||||
"phones",
|
||||
Person.class.getDeclaredField("phones").getGenericType(),
|
||||
"Wrong meta model type"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12338")
|
||||
@WithClasses({PersonPhone.class})
|
||||
public void testListTypeWithImport() throws ClassNotFoundException, NoSuchFieldException {
|
||||
assertMetamodelClassGeneratedFor(PersonPhone.class);
|
||||
|
||||
assertAttributeTypeInMetaModelFor(
|
||||
PersonPhone.class,
|
||||
"phones",
|
||||
PersonPhone.class.getDeclaredField("phones").getGenericType(),
|
||||
"Wrong meta model type"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12338")
|
||||
@WithClasses({PhoneBook.class})
|
||||
public void testMapType() throws ClassNotFoundException, NoSuchFieldException {
|
||||
assertMetamodelClassGeneratedFor(PhoneBook.class);
|
||||
|
||||
assertAttributeTypeInMetaModelFor(
|
||||
PhoneBook.class,
|
||||
"phones",
|
||||
PhoneBook.class.getDeclaredField("phones").getGenericType(),
|
||||
"Wrong meta model type"
|
||||
);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CommaDelimitedStringMapJavaTypeDescriptor extends AbstractTypeDescriptor<Map> {
|
||||
|
||||
public static final String DELIMITER = ",";
|
||||
|
||||
public CommaDelimitedStringMapJavaTypeDescriptor() {
|
||||
super(
|
||||
Map.class,
|
||||
new MutableMutabilityPlan<Map>() {
|
||||
@Override
|
||||
protected Map deepCopyNotNull(Map value) {
|
||||
return new HashMap( value );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Map value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(Map value, Class<X> type, WrapperOptions options) {
|
||||
return (X) toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> Map wrap(X value, WrapperOptions options) {
|
||||
return fromString( (String) value );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CommaDelimitedStringsJavaTypeDescriptor extends AbstractTypeDescriptor<List> {
|
||||
|
||||
public static final String DELIMITER = ",";
|
||||
|
||||
public CommaDelimitedStringsJavaTypeDescriptor() {
|
||||
super(
|
||||
List.class,
|
||||
new MutableMutabilityPlan<List>() {
|
||||
@Override
|
||||
protected List deepCopyNotNull(List value) {
|
||||
return new ArrayList( value );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(List value) {
|
||||
return ( (List<String>) value ).stream().collect( Collectors.joining( DELIMITER ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List fromString(String string) {
|
||||
List<String> values = new ArrayList<>();
|
||||
Collections.addAll( values, string.split( DELIMITER ) );
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(List value, Class<X> type, WrapperOptions options) {
|
||||
return (X) toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> List wrap(X value, WrapperOptions options) {
|
||||
return fromString( (String) value );
|
||||
}
|
||||
}
|
|
@ -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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
|
||||
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CommaDelimitedStringsMapType extends AbstractSingleColumnStandardBasicType<Map> {
|
||||
|
||||
public CommaDelimitedStringsMapType() {
|
||||
super(
|
||||
VarcharTypeDescriptor.INSTANCE,
|
||||
new CommaDelimitedStringMapJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "comma_delimited_string_map";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
|
||||
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class CommaDelimitedStringsType extends AbstractSingleColumnStandardBasicType<List> {
|
||||
|
||||
public CommaDelimitedStringsType() {
|
||||
super(
|
||||
VarcharTypeDescriptor.INSTANCE,
|
||||
new CommaDelimitedStringsJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "comma_delimited_strings";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author helloztt
|
||||
*/
|
||||
@Entity
|
||||
public class Goods {
|
||||
private Long id;
|
||||
private List<Product> productList;
|
||||
private List<String> tags;
|
||||
|
||||
@Id
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToMany
|
||||
public List<Product> getProductList() {
|
||||
return productList;
|
||||
}
|
||||
|
||||
public void setProductList(List<Product> productList) {
|
||||
this.productList = productList;
|
||||
}
|
||||
|
||||
@Convert(converter = StringToListConverter.class)
|
||||
public List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.hibernate.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@Entity(name = "Person")
|
||||
@TypeDef( name = "comma_delimited_strings", typeClass = CommaDelimitedStringsType.class)
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Type(type = "comma_delimited_strings")
|
||||
private List<String> phones = new ArrayList<>();
|
||||
|
||||
public List<String> getPhones() {
|
||||
return phones;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.hibernate.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
import org.hibernate.jpamodelgen.test.collectionbasictype.extras.Phone;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@Entity(name = "Person")
|
||||
@TypeDef( name = "comma_delimited_strings", typeClass = CommaDelimitedStringsType.class)
|
||||
public class PersonPhone {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Type(type = "comma_delimited_strings")
|
||||
private List<Phone> phones = new ArrayList<>();
|
||||
|
||||
public List<Phone> getPhones() {
|
||||
return phones;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.hibernate.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@Entity(name = "Person")
|
||||
@TypeDef( name = "comma_delimited_string_map", typeClass = CommaDelimitedStringsMapType.class)
|
||||
public class PhoneBook {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Type(type = "comma_delimited_string_map")
|
||||
private Map<String, String> phones = new HashMap<>();
|
||||
|
||||
public Map<String, String> getPhones() {
|
||||
return phones;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author helloztt
|
||||
*/
|
||||
@Entity
|
||||
public class Product {
|
||||
private int proId;
|
||||
private String proName;
|
||||
|
||||
@Id
|
||||
public int getProId() {
|
||||
return proId;
|
||||
}
|
||||
|
||||
public void setProId(int proId) {
|
||||
this.proId = proId;
|
||||
}
|
||||
|
||||
public String getProName() {
|
||||
return proName;
|
||||
}
|
||||
|
||||
public void setProName(String proName) {
|
||||
this.proName = proName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.jpamodelgen.test.collectionbasictype;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author helloztt
|
||||
*/
|
||||
public class StringToListConverter implements AttributeConverter<List<String>, String> {
|
||||
private static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(List<String> attribute) {
|
||||
if (attribute == null || attribute.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
return attribute.stream().collect(Collectors.joining(COMMA));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> convertToEntityAttribute(String dbData) {
|
||||
if (dbData == null || dbData.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.asList(dbData.split(COMMA));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.jpamodelgen.test.collectionbasictype.extras;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class Phone implements Serializable {
|
||||
|
||||
private String phoneNumber;
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
}
|
|
@ -101,6 +101,18 @@ public class TestUtil {
|
|||
);
|
||||
}
|
||||
|
||||
public static void assertAttributeTypeInMetaModelFor(Class<?> clazz, String fieldName, Type expectedType, String errorString) {
|
||||
Field field = getFieldFromMetamodelFor( clazz, fieldName );
|
||||
assertNotNull( "Cannot find field '" + fieldName + "' in " + clazz.getName(), field );
|
||||
ParameterizedType type = (ParameterizedType) field.getGenericType();
|
||||
Type actualType = type.getActualTypeArguments()[1];
|
||||
assertEquals(
|
||||
"Types do not match: " + buildErrorString( errorString, clazz ),
|
||||
expectedType,
|
||||
actualType
|
||||
);
|
||||
}
|
||||
|
||||
public static void assertListAttributeTypeInMetaModelFor(Class<?> clazz, String fieldName, Class<?> expectedType, String errorString) {
|
||||
Field field = getFieldFromMetamodelFor( clazz, fieldName );
|
||||
assertNotNull( "Cannot find field '" + fieldName + "' in " + clazz.getName(), field );
|
||||
|
|
Loading…
Reference in New Issue