Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions documentation/manual/src/main/docbook/en-US/content/type.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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" };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,7 +36,7 @@
*
* @author Emmanuel Bernard
*/
@Target({METHOD, FIELD})
@Target({METHOD, FIELD, TYPE})
@Retention(RUNTIME)
public @interface Columns {
Column[] columns();
Expand Down
18 changes: 17 additions & 1 deletion hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -422,6 +426,18 @@ public static Ejb3Column[] buildColumnFromAnnotation(
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,
Expand All @@ -442,7 +458,7 @@ public static Ejb3Column[] buildColumnFromAnnotation(
? 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 );
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}

}