Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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

Open
wants to merge 1 commit into from

2 participants

Frode Carlsen Brett Meyer
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

HHH-4347
Adding defaults for mapping column name and properties for a CustomUserType
545c8eb
Brett Meyer
Collaborator

On hold for 5.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Nov 04, 2010
HHH-4347
Adding defaults for mapping column name and properties for a CustomUserType
545c8eb
This page is out of date. Refresh to see the latest.
11  documentation/manual/src/main/docbook/en-US/content/type.xml
@@ -905,10 +905,17 @@ cfg...;
905 905
                 <interfacename>org.hibernate.usertype.CompositeUserType</interfacename>, our
906 906
                 <classname>Money</classname> custom type would look as follows:
907 907
             </para>
908  
-
  908
+            <para>
  909
+                The <interfacename>org.hibernate.usertype.CompositeUserType</interfacename> may be annotated with a @Columns definition to provide
  910
+                a default mapping of column names and column properties.  This allows the author to specify defaults for the mapping, in order to  
  911
+                reduce the burden of mapping each occurrence of the given type.  If a name is specified in the @Column definition of the CustomUserType, 
  912
+                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 
  913
+                may be overridden by adding a mapping on the property where the type is used.
  914
+            </para>
909 915
             <example id="types-custom-cut-ex-definition">
910 916
                 <title>Defining the custom CompositeUserType</title>
911  
-                <programlisting role="JAVA"><![CDATA[public class MoneyType implements CompositeUserType {
  917
+                <programlisting role="JAVA"><![CDATA[@Columns(columns = { @Column(name = "amount", precision = 19), @Column(name = "ccy", length=3, nullable = false) })
  918
+                public class MoneyType implements CompositeUserType {
912 919
     public String[] getPropertyNames() {
913 920
         // ORDER IS IMPORTANT!  it must match the order the columns are defined in the property mapping
914 921
         return new String[] { "amount", "currency" };
3  hibernate-core/src/main/java/org/hibernate/annotations/Columns.java
@@ -25,6 +25,7 @@
25 25
 
26 26
 import static java.lang.annotation.ElementType.FIELD;
27 27
 import static java.lang.annotation.ElementType.METHOD;
  28
+import static java.lang.annotation.ElementType.TYPE;
28 29
 import java.lang.annotation.Retention;
29 30
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
30 31
 import java.lang.annotation.Target;
@@ -35,7 +36,7 @@
35 36
  *
36 37
  * @author Emmanuel Bernard
37 38
  */
38  
-@Target({METHOD, FIELD})
  39
+@Target({METHOD, FIELD, TYPE})
39 40
 @Retention(RUNTIME)
40 41
 public @interface Columns {
41 42
 	Column[] columns();
18  hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
@@ -29,6 +29,7 @@
29 29
 import org.hibernate.AssertionFailure;
30 30
 import org.hibernate.annotations.ColumnTransformer;
31 31
 import org.hibernate.annotations.ColumnTransformers;
  32
+import org.hibernate.annotations.Columns;
32 33
 import org.hibernate.annotations.Index;
33 34
 import org.hibernate.annotations.common.reflection.XProperty;
34 35
 import org.hibernate.cfg.annotations.Nullability;
@@ -37,6 +38,9 @@
37 38
 import org.hibernate.mapping.Join;
38 39
 import org.hibernate.mapping.SimpleValue;
39 40
 import org.hibernate.mapping.Table;
  41
+import org.hibernate.type.BasicType;
  42
+import org.hibernate.type.CompositeCustomType;
  43
+import org.hibernate.usertype.CompositeUserType;
40 44
 import org.hibernate.util.StringHelper;
41 45
 import org.slf4j.LoggerFactory;
42 46
 import org.slf4j.Logger;
@@ -422,6 +426,18 @@ public void setSecondaryTableName(String secondaryTableName) {
422 426
 				actualCols = overriddenCols.length == 0 ? null : overriddenCols;
423 427
 				log.debug( "Column(s) overridden for property {}", inferredData.getPropertyName() );
424 428
 			}
  429
+			// get column definition from CustomUserType implementation
  430
+            String columnNamePrefix = "";
  431
+			if ( actualCols == null ) {
  432
+                BasicType basicType = mappings.getTypeResolver().basic(inferredData.getClassOrElementName());
  433
+                if (basicType != null && CompositeCustomType.class.isAssignableFrom(basicType.getClass())) {
  434
+                    CompositeUserType compositeUserType = ((CompositeCustomType) basicType).getUserType();
  435
+                    if (compositeUserType.getClass().isAnnotationPresent(Columns.class)) {
  436
+                        columnNamePrefix = inferredData.getPropertyName() + "_";
  437
+                        actualCols = compositeUserType.getClass().getAnnotation(Columns.class).columns();
  438
+                    }
  439
+                }
  440
+            }
425 441
 			if ( actualCols == null ) {
426 442
 				columns = buildImplicitColumn(
427 443
 						inferredData,
@@ -442,7 +458,7 @@ public void setSecondaryTableName(String secondaryTableName) {
442 458
 							? null
443 459
 							: nameNormalizer.normalizeIdentifierQuoting( col.columnDefinition() );
444 460
 					final String tableName = nameNormalizer.normalizeIdentifierQuoting( col.table() );
445  
-					final String columnName = nameNormalizer.normalizeIdentifierQuoting( col.name() );
  461
+					final String columnName = nameNormalizer.normalizeIdentifierQuoting( columnNamePrefix + col.name() );
446 462
 					Ejb3Column column = new Ejb3Column();
447 463
 					column.setImplicit( false );
448 464
 					column.setSqlType( sqlType );
29  hibernate-core/src/test/java/org/hibernate/test/annotations/cut/Address.java
... ...
@@ -0,0 +1,29 @@
  1
+//$Id: Address.java 14736 2008-06-04 14:23:42Z hardy.ferentschik $
  2
+package org.hibernate.test.annotations.cut;
  3
+
  4
+import java.io.Serializable;
  5
+
  6
+public class Address implements Serializable {
  7
+	private static final long serialVersionUID = 1L;
  8
+
  9
+	String address1;
  10
+
  11
+    String city;
  12
+    
  13
+
  14
+    public String getAddress1() {
  15
+        return address1;
  16
+    }
  17
+
  18
+    public String getCity() {
  19
+        return city;
  20
+    }
  21
+
  22
+    public void setAddress1(String address1) {
  23
+        this.address1 = address1;
  24
+    }
  25
+
  26
+    public void setCity(String city) {
  27
+        this.city = city;
  28
+    }
  29
+}
127  hibernate-core/src/test/java/org/hibernate/test/annotations/cut/AddressCompositeUserType.java
... ...
@@ -0,0 +1,127 @@
  1
+package org.hibernate.test.annotations.cut;
  2
+
  3
+import java.io.Serializable;
  4
+import java.sql.PreparedStatement;
  5
+import java.sql.ResultSet;
  6
+import java.sql.SQLException;
  7
+
  8
+import javax.persistence.Column;
  9
+
  10
+import org.hibernate.HibernateException;
  11
+import org.hibernate.annotations.Columns;
  12
+import org.hibernate.engine.SessionImplementor;
  13
+import org.hibernate.type.StringType;
  14
+import org.hibernate.type.Type;
  15
+import org.hibernate.usertype.CompositeUserType;
  16
+
  17
+/**
  18
+ * @author Frode Carlsen
  19
+ */
  20
+@Columns(columns = { @Column(name = "addressLine1"), @Column(name = "cityName") })
  21
+public class AddressCompositeUserType implements CompositeUserType {
  22
+
  23
+    public static final AddressCompositeUserType INSTANCE = new AddressCompositeUserType();
  24
+    private static final String[] PROPERTY_NAMES = new String[] { "addr1", "city" };
  25
+
  26
+    private static final Type[] TYPES = new Type[] { StringType.INSTANCE, StringType.INSTANCE };
  27
+
  28
+    public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
  29
+        return cached;
  30
+    }
  31
+
  32
+    public Object deepCopy(Object value) throws HibernateException {
  33
+        return value;
  34
+    }
  35
+
  36
+    public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
  37
+        return (Serializable) value;
  38
+    }
  39
+
  40
+    public boolean equals(Object x, Object y) throws HibernateException {
  41
+        if (x == y) {
  42
+            return true;
  43
+        }
  44
+        if (x == null || y == null) {
  45
+            return false;
  46
+        }
  47
+        return x.equals(y);
  48
+    }
  49
+
  50
+    public String[] getPropertyNames() {
  51
+        return PROPERTY_NAMES;
  52
+    }
  53
+
  54
+    public Type[] getPropertyTypes() {
  55
+        return TYPES;
  56
+    }
  57
+
  58
+    public Object getPropertyValue(Object component, int propertyIndex) throws HibernateException {
  59
+        Address address = (Address) component;
  60
+        switch (propertyIndex) {
  61
+        case 0:
  62
+            return address.address1;
  63
+        case 1:
  64
+            return address.city;
  65
+        default:
  66
+            return null;
  67
+        }
  68
+    }
  69
+
  70
+    public int hashCode(Object x) throws HibernateException {
  71
+        return x.hashCode();
  72
+    }
  73
+
  74
+    public boolean isMutable() {
  75
+        return false;
  76
+    }
  77
+
  78
+    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
  79
+            throws HibernateException, SQLException {
  80
+        if (resultSet == null) {
  81
+            return null;
  82
+        }
  83
+        Address address = new Address();
  84
+        String address1 = resultSet.getString(names[0]);
  85
+        String city = resultSet.getString(names[1]);
  86
+        if (address1 == null && city == null) {
  87
+            return null;
  88
+        }
  89
+        address.address1 = address1;
  90
+        address.city = city;
  91
+        return address;
  92
+    }
  93
+
  94
+    public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session)
  95
+            throws HibernateException, SQLException {
  96
+        if (value == null) {
  97
+            statement.setNull(index, StringType.INSTANCE.sqlType());
  98
+            statement.setNull(index + 1, StringType.INSTANCE.sqlType());
  99
+            return;
  100
+        }
  101
+        Address address = (Address) value;
  102
+        statement.setString(index, address.address1);
  103
+        statement.setString(index + 1, address.city);
  104
+    }
  105
+
  106
+    public Object replace(Object original, Object target, SessionImplementor session, Object owner)
  107
+            throws HibernateException {
  108
+        return original;
  109
+    }
  110
+
  111
+    public Class<?> returnedClass() {
  112
+        return Address.class;
  113
+    }
  114
+
  115
+    public void setPropertyValue(Object component, int propertyIndex, Object value) throws HibernateException {
  116
+        Address address = (Address) component;
  117
+        switch (propertyIndex) {
  118
+        case 0:
  119
+            address.address1 = (String) value;
  120
+            break;
  121
+        case 1:
  122
+            address.city = (String) value;
  123
+        default:
  124
+            break;
  125
+        }
  126
+    }
  127
+}
69  hibernate-core/src/test/java/org/hibernate/test/annotations/cut/CompositeUserTypePropertyNameTest.java
... ...
@@ -0,0 +1,69 @@
  1
+package org.hibernate.test.annotations.cut;
  2
+
  3
+import java.sql.Connection;
  4
+import java.sql.SQLException;
  5
+
  6
+import org.hibernate.Session;
  7
+import org.hibernate.SessionFactory;
  8
+import org.hibernate.cfg.Configuration;
  9
+import org.hibernate.jdbc.Work;
  10
+import org.hibernate.test.annotations.TestCase;
  11
+import org.hibernate.tool.hbm2ddl.SchemaExport;
  12
+
  13
+/**
  14
+ * @author Frode Carlsen
  15
+ */
  16
+public class CompositeUserTypePropertyNameTest extends TestCase {
  17
+
  18
+    @Override
  19
+    protected Class<?>[] getAnnotatedClasses() {
  20
+        return new Class[] { Person.class };
  21
+    }
  22
+
  23
+    @Override
  24
+    protected void configure(Configuration cfg) {
  25
+        super.configure(cfg);
  26
+        cfg.registerTypeOverride(AddressCompositeUserType.INSTANCE, new String[] { Address.class.getName() });
  27
+    }
  28
+
  29
+    @Override
  30
+    protected void setUp() throws Exception {
  31
+        super.setUp();
  32
+        exportSchema(cfg, getSessions());
  33
+    }
  34
+
  35
+    private static void exportSchema(final Configuration cfg, SessionFactory sessFact) {
  36
+        org.hibernate.classic.Session session = sessFact.openSession();
  37
+        session.doWork(new Work() {
  38
+            public void execute(final Connection conn) throws SQLException {
  39
+                SchemaExport schemaExport = new SchemaExport(cfg, conn);
  40
+                schemaExport.create(true, true);
  41
+            }
  42
+        });
  43
+        session.close();
  44
+    }
  45
+
  46
+    public void testBasicOps() {
  47
+        Session session = openSession();
  48
+        session.beginTransaction();
  49
+        
  50
+        Person person = new Person("Steve", new Address());
  51
+        person.getAddress().setAddress1("123 Main");
  52
+        person.getAddress().setCity("Anywhere");
  53
+        
  54
+        session.persist(person);
  55
+        session.getTransaction().commit();
  56
+        session.close();
  57
+
  58
+        session = openSession();
  59
+        session.beginTransaction();
  60
+        Person person1 = (Person) session.createQuery("from Person p where p.address.addr1 = '123 Main'").uniqueResult();
  61
+        assertTrue(person != person1);
  62
+        session.createQuery("from Person p where p.address.city = 'Anywhere'").list();
  63
+        person = (Person) session.load(Person.class, person.getId());
  64
+        session.delete(person);
  65
+
  66
+        session.getTransaction().commit();
  67
+        session.close();
  68
+    }
  69
+}
55  hibernate-core/src/test/java/org/hibernate/test/annotations/cut/Person.java
... ...
@@ -0,0 +1,55 @@
  1
+//$Id: Person.java 14736 2008-06-04 14:23:42Z hardy.ferentschik $
  2
+package org.hibernate.test.annotations.cut;
  3
+
  4
+import java.io.Serializable;
  5
+
  6
+import javax.persistence.Entity;
  7
+import javax.persistence.GeneratedValue;
  8
+import javax.persistence.Id;
  9
+
  10
+@Entity
  11
+public class Person implements Serializable {
  12
+	private static final long serialVersionUID = 1L;
  13
+
  14
+	@Id
  15
+    @GeneratedValue
  16
+    Integer id;
  17
+
  18
+    String name;
  19
+    
  20
+    Address address;
  21
+
  22
+    @SuppressWarnings("unused")
  23
+    private Person() {
  24
+    }
  25
+
  26
+    public Person(String name, Address address) {
  27
+        this.name = name;
  28
+        this.address = address;
  29
+    }
  30
+
  31
+    public Address getAddress() {
  32
+        return address;
  33
+    }
  34
+
  35
+    public Integer getId() {
  36
+        return id;
  37
+    }
  38
+
  39
+    public String getName() {
  40
+        return name;
  41
+    }
  42
+
  43
+    public void setAddress(Address address) {
  44
+        this.address = address;
  45
+    }
  46
+
  47
+    public void setId(Integer id) {
  48
+        this.id = id;
  49
+    }
  50
+
  51
+    public void setName(String name) {
  52
+        this.name = name;
  53
+    }
  54
+
  55
+}
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.