Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

HHH-4347 default column names/properties on CompositeUserType #11

Closed
wants to merge 1 commit into from

3 participants

@frode-carlsen

I've created a small patch to allow a CompositeUserType to define a default column mapping (using @Columns and @Column annotations), which will be concatenated with the name of the property to give unique names for an entity.

I opted to allow the @Column annotations instead of the getPropertyNames(), as it is conceptually the same as using these on the property in the entity containing the type today, and it allows for defining other defaults as well (length, precision, nullable, etc).
This means that when annotations are used, they are handled the same whether they are defined on the property (as today) or defined by default on the CompositeUserType, with the only difference being that in the latter case the name defined on the @Column annotation will be prefixed by the name of the property. Also, column names are allowed to be different to the names used in queries, at the implementor's discretion.

The patch is backwards-compatible so if any CompositeUserType does not define any of these annotations then everything works the same as before. Also, any @Columns/@Column annotations on a property in an entity will override the default defined.

Regards,
Frode Carlsen

frodecarlsen HHH-4347
Adding defaults for mapping column name and properties for a CustomUserType
545c8eb
@brmeyer
Collaborator

On hold for 5.0.0

@sebersole sebersole closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 4, 2010
  1. HHH-4347

    frodecarlsen authored
    Adding defaults for mapping column name and properties for a CustomUserType
This page is out of date. Refresh to see the latest.
View
11 documentation/manual/src/main/docbook/en-US/content/type.xml
@@ -905,10 +905,17 @@ cfg...;
<interfacename>org.hibernate.usertype.CompositeUserType</interfacename>, our
<classname>Money</classname> custom type would look as follows:
</para>
-
+ <para>
+ The <interfacename>org.hibernate.usertype.CompositeUserType</interfacename> may be annotated with a @Columns definition to provide
+ a default mapping of column names and column properties. This allows the author to specify defaults for the mapping, in order to
+ reduce the burden of mapping each occurrence of the given type. If a name is specified in the @Column definition of the CustomUserType,
+ this will be prefixed with the name of the property to give a fully unique name for the mapping. Any defaults specified in this way
+ may be overridden by adding a mapping on the property where the type is used.
+ </para>
<example id="types-custom-cut-ex-definition">
<title>Defining the custom CompositeUserType</title>
- <programlisting role="JAVA"><![CDATA[public class MoneyType implements CompositeUserType {
+ <programlisting role="JAVA"><![CDATA[@Columns(columns = { @Column(name = "amount", precision = 19), @Column(name = "ccy", length=3, nullable = false) })
+ public class MoneyType implements CompositeUserType {
public String[] getPropertyNames() {
// ORDER IS IMPORTANT! it must match the order the columns are defined in the property mapping
return new String[] { "amount", "currency" };
View
3  hibernate-core/src/main/java/org/hibernate/annotations/Columns.java
@@ -25,6 +25,7 @@
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
@@ -35,7 +36,7 @@
*
* @author Emmanuel Bernard
*/
-@Target({METHOD, FIELD})
+@Target({METHOD, FIELD, TYPE})
@Retention(RUNTIME)
public @interface Columns {
Column[] columns();
View
18 hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
@@ -29,6 +29,7 @@
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
+import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.annotations.Nullability;
@@ -37,6 +38,9 @@
import org.hibernate.mapping.Join;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
+import org.hibernate.type.BasicType;
+import org.hibernate.type.CompositeCustomType;
+import org.hibernate.usertype.CompositeUserType;
import org.hibernate.util.StringHelper;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
@@ -422,6 +426,18 @@ public void setSecondaryTableName(String secondaryTableName) {
actualCols = overriddenCols.length == 0 ? null : overriddenCols;
log.debug( "Column(s) overridden for property {}", inferredData.getPropertyName() );
}
+ // get column definition from CustomUserType implementation
+ String columnNamePrefix = "";
+ if ( actualCols == null ) {
+ BasicType basicType = mappings.getTypeResolver().basic(inferredData.getClassOrElementName());
+ if (basicType != null && CompositeCustomType.class.isAssignableFrom(basicType.getClass())) {
+ CompositeUserType compositeUserType = ((CompositeCustomType) basicType).getUserType();
+ if (compositeUserType.getClass().isAnnotationPresent(Columns.class)) {
+ columnNamePrefix = inferredData.getPropertyName() + "_";
+ actualCols = compositeUserType.getClass().getAnnotation(Columns.class).columns();
+ }
+ }
+ }
if ( actualCols == null ) {
columns = buildImplicitColumn(
inferredData,
@@ -442,7 +458,7 @@ public void setSecondaryTableName(String secondaryTableName) {
? null
: nameNormalizer.normalizeIdentifierQuoting( col.columnDefinition() );
final String tableName = nameNormalizer.normalizeIdentifierQuoting( col.table() );
- final String columnName = nameNormalizer.normalizeIdentifierQuoting( col.name() );
+ final String columnName = nameNormalizer.normalizeIdentifierQuoting( columnNamePrefix + col.name() );
Ejb3Column column = new Ejb3Column();
column.setImplicit( false );
column.setSqlType( sqlType );
View
29 hibernate-core/src/test/java/org/hibernate/test/annotations/cut/Address.java
@@ -0,0 +1,29 @@
+//$Id: Address.java 14736 2008-06-04 14:23:42Z hardy.ferentschik $
+package org.hibernate.test.annotations.cut;
+
+import java.io.Serializable;
+
+public class Address implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ String address1;
+
+ String city;
+
+
+ public String getAddress1() {
+ return address1;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setAddress1(String address1) {
+ this.address1 = address1;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+}
View
127 ...rnate-core/src/test/java/org/hibernate/test/annotations/cut/AddressCompositeUserType.java
@@ -0,0 +1,127 @@
+package org.hibernate.test.annotations.cut;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import javax.persistence.Column;
+
+import org.hibernate.HibernateException;
+import org.hibernate.annotations.Columns;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.StringType;
+import org.hibernate.type.Type;
+import org.hibernate.usertype.CompositeUserType;
+
+/**
+ * @author Frode Carlsen
+ */
+@Columns(columns = { @Column(name = "addressLine1"), @Column(name = "cityName") })
+public class AddressCompositeUserType implements CompositeUserType {
+
+ public static final AddressCompositeUserType INSTANCE = new AddressCompositeUserType();
+ private static final String[] PROPERTY_NAMES = new String[] { "addr1", "city" };
+
+ private static final Type[] TYPES = new Type[] { StringType.INSTANCE, StringType.INSTANCE };
+
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
+ return cached;
+ }
+
+ public Object deepCopy(Object value) throws HibernateException {
+ return value;
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
+ return (Serializable) value;
+ }
+
+ public boolean equals(Object x, Object y) throws HibernateException {
+ if (x == y) {
+ return true;
+ }
+ if (x == null || y == null) {
+ return false;
+ }
+ return x.equals(y);
+ }
+
+ public String[] getPropertyNames() {
+ return PROPERTY_NAMES;
+ }
+
+ public Type[] getPropertyTypes() {
+ return TYPES;
+ }
+
+ public Object getPropertyValue(Object component, int propertyIndex) throws HibernateException {
+ Address address = (Address) component;
+ switch (propertyIndex) {
+ case 0:
+ return address.address1;
+ case 1:
+ return address.city;
+ default:
+ return null;
+ }
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ return x.hashCode();
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ if (resultSet == null) {
+ return null;
+ }
+ Address address = new Address();
+ String address1 = resultSet.getString(names[0]);
+ String city = resultSet.getString(names[1]);
+ if (address1 == null && city == null) {
+ return null;
+ }
+ address.address1 = address1;
+ address.city = city;
+ return address;
+ }
+
+ public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+ if (value == null) {
+ statement.setNull(index, StringType.INSTANCE.sqlType());
+ statement.setNull(index + 1, StringType.INSTANCE.sqlType());
+ return;
+ }
+ Address address = (Address) value;
+ statement.setString(index, address.address1);
+ statement.setString(index + 1, address.city);
+ }
+
+ public Object replace(Object original, Object target, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return original;
+ }
+
+ public Class<?> returnedClass() {
+ return Address.class;
+ }
+
+ public void setPropertyValue(Object component, int propertyIndex, Object value) throws HibernateException {
+ Address address = (Address) component;
+ switch (propertyIndex) {
+ case 0:
+ address.address1 = (String) value;
+ break;
+ case 1:
+ address.city = (String) value;
+ default:
+ break;
+ }
+ }
+}
View
69 ...e/src/test/java/org/hibernate/test/annotations/cut/CompositeUserTypePropertyNameTest.java
@@ -0,0 +1,69 @@
+package org.hibernate.test.annotations.cut;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.jdbc.Work;
+import org.hibernate.test.annotations.TestCase;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+/**
+ * @author Frode Carlsen
+ */
+public class CompositeUserTypePropertyNameTest extends TestCase {
+
+ @Override
+ protected Class<?>[] getAnnotatedClasses() {
+ return new Class[] { Person.class };
+ }
+
+ @Override
+ protected void configure(Configuration cfg) {
+ super.configure(cfg);
+ cfg.registerTypeOverride(AddressCompositeUserType.INSTANCE, new String[] { Address.class.getName() });
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ exportSchema(cfg, getSessions());
+ }
+
+ private static void exportSchema(final Configuration cfg, SessionFactory sessFact) {
+ org.hibernate.classic.Session session = sessFact.openSession();
+ session.doWork(new Work() {
+ public void execute(final Connection conn) throws SQLException {
+ SchemaExport schemaExport = new SchemaExport(cfg, conn);
+ schemaExport.create(true, true);
+ }
+ });
+ session.close();
+ }
+
+ public void testBasicOps() {
+ Session session = openSession();
+ session.beginTransaction();
+
+ Person person = new Person("Steve", new Address());
+ person.getAddress().setAddress1("123 Main");
+ person.getAddress().setCity("Anywhere");
+
+ session.persist(person);
+ session.getTransaction().commit();
+ session.close();
+
+ session = openSession();
+ session.beginTransaction();
+ Person person1 = (Person) session.createQuery("from Person p where p.address.addr1 = '123 Main'").uniqueResult();
+ assertTrue(person != person1);
+ session.createQuery("from Person p where p.address.city = 'Anywhere'").list();
+ person = (Person) session.load(Person.class, person.getId());
+ session.delete(person);
+
+ session.getTransaction().commit();
+ session.close();
+ }
+}
View
55 hibernate-core/src/test/java/org/hibernate/test/annotations/cut/Person.java
@@ -0,0 +1,55 @@
+//$Id: Person.java 14736 2008-06-04 14:23:42Z hardy.ferentschik $
+package org.hibernate.test.annotations.cut;
+
+import java.io.Serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Person implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue
+ Integer id;
+
+ String name;
+
+ Address address;
+
+ @SuppressWarnings("unused")
+ private Person() {
+ }
+
+ public Person(String name, Address address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
Something went wrong with that request. Please try again.