【问题标题】:Hibernate annotation object mappingHibernate注解对象映射
【发布时间】:2011-11-10 18:00:36
【问题描述】:

我对 hibernate 很陌生,我正在尝试将我拥有的 JDBC 项目转换为 Hibernate。

我正在使用注释,并且我设法注释了基本的东西,但是,我现在被更重的对象困住了,我不知道如何注释它们。 这是课程:

@Entity
@Table(name = "person")
public class Person {

    public Person{

    }

    // THIS WILL BE SOON INJECTED BY SPRING
    private static transient PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    private static transient EmailValidator validator = EmailValidator.getInstance();

    @Id
    @Column(name = "person_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "private_name", nullable = false, length = 20)
    private String privateName;

    @Column(name = "middle_name", length = 20)
    private String middleName;

    @Column(name = "family_name", nullable = false, length = 20)
    private String familyName;

    @Column(name = "age", nullable = false)
    private int age;

    @Column(name = "address1", nullable = false)
    private String address1;

    @Column(name = "address2")
    private String address2;

    //How do I annotate this ? --> Google LIBPHONENUMBER

    private PhoneNumber phone;

    // How do I annotate this ? --> This is a normal PNG image file.
    private File image;

编辑: 该文件以前被映射为 BLOB。 PhoneNumber 之前以字符串形式保存,并使用 PhoneNumber 构造函数转换为 Phonenumber。

【问题讨论】:

  • 文件如何存储在数据库中?作为 BLOB?
  • @Lob 是休眠表示法。问候!

标签: java hibernate annotations dao


【解决方案1】:

关于使用@Lob 的其他cmets 对于文件类型是正确的。同样正确的是,如果您可以更改架构以不将文件数据保存在数据库中,那么您可能应该这样做。

要将您的 PhoneNumber 类映射到数据库字段,您将需要使用 Hibernate 自定义 UserType。它基本上告诉 Hibernate 如何为它还不知道的类做对象db 映射。告诉 Person 中的 PhoneNumber 字段使用自定义用户类型很容易:

@Type(type = PhoneNumberType.CLASS_NAME)
@Column
private PhoneNumber phone;

这假设电话号码的一栏存储非常简单。

要编写 PhoneNumberType,您需要实现 UserType。用 assemble/disassemble/deepCopy 看起来势不可挡,但您关心的主要部分是 nullSetGet/Set、returnedClass 和 sqlTypes。你最终会在你的自定义类型中得到一些这样的代码:

@Override
public Class<?> returnedClass() {
    return PhoneNumber.class;
}

@Override
public int[] sqlTypes() {
    return new int[] { Types.VARCHAR };
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
    final String value = rs.getString(names[0]);
    return /* PhoneNumber instance created from string. */
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
    if (value == null) {
        st.setNull(index, Types.VARBINARY);
        return;
    }

    st.setString(index, ((PhoneNumber) value).toString());
}

您可以通过 google、stackoverflow 和 hibernate javadocs 找到有关如何实现其他方法的大量信息。这并不难。

更新:多列用户类型

实现CompositeUserType 而不仅仅是UserType。有一些您关心的方法更改。首先,您需要定义多个属性名称和类型:

public String[] getPropertyNames() {
    return new String[] { "number", "code" };
}

public Type[] getPropertyTypes() {
    return new Type[] { StandardBasicTypes.STRING,
                        StandardBasicTypes.STRING };
}

还有getPropertyValue/setPropertyValue 来实现。您的 nullSafeXxxx 实现将更改为读取和写入两个属性,而不是一个:

@Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
    // Access column in order defined in getPropertyNames()
    final String number = rs.getString(names[0]);
    final String code = rs.getString(names[1]);
    return /* PhoneNumber instance created from number and country code. */
}

【讨论】:

  • 我检查了,这很容易,但我有一个问题。要构造一个电话号码,我需要向构造函数传递一个代表号码的字符串和一个国家/地区。国家代码是不同的列。我该如何解决?
  • 什么是 PhoneNumberType.CLASS_NAME ?你从来没有定义过。
  • 它只是 PhoneNumberType 的完全限定类名的静态最终字符串
【解决方案2】:

就个人而言,我只会将文件名存储在对象中,并将文件保存在文件所属的文件系统上。

否则,将其映射为 Hibernate blob (@Lob),并且您希望它是字节数组(将转换为 blob)。

IMO 这通常会带来比其价值更多的麻烦,但这部分取决于数据库、驱动程序修订等。

【讨论】:

    【解决方案3】:

    只需为 PhoneNumber 创建一个 Hibernate UserType

    import java.io.Serializable;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Types;
    
    import org.apache.commons.lang.ObjectUtils;
    import org.hibernate.HibernateException;
    import org.hibernate.engine.spi.SessionImplementor;
    import org.hibernate.type.StringRepresentableType;
    import org.hibernate.usertype.UserType;
    
    import com.google.i18n.phonenumbers.NumberParseException;
    import com.google.i18n.phonenumbers.PhoneNumberUtil;
    import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
    import com.tg.util.TGPhoneUtils;
    
    public class PhoneNumberUserType implements UserType, StringRepresentableType<PhoneNumber>, Serializable {
        private static final long serialVersionUID = -364436436346432L;
    
        @Override
        public boolean equals(Object x, Object y) throws HibernateException {
            return ObjectUtils.equals(x, y);
        }
    
        @Override
        public int hashCode(Object object) throws HibernateException {
            return object.hashCode();
        }
    
        @Override
        public Object deepCopy(Object value) throws HibernateException {
            return value;
        }
    
        @Override
        public boolean isMutable() {
            return false;
        }
    
        @Override
        public Serializable disassemble(Object value) throws HibernateException {
            return (Serializable) value;
        }
    
        @Override
        public Object assemble(Serializable cached, Object value) throws HibernateException {
            return cached;
        }
    
        @Override
        public Object replace(Object original, Object target, Object owner) throws HibernateException {
            return original;
        }
    
        @Override
        public String toString(PhoneNumber value) throws HibernateException {
            return value.toString();
        }
    
        @Override
        public Class<?> returnedClass() {
            return PhoneNumber.class;
        }
    
        @Override
        public int[] sqlTypes() {
            return new int[] { Types.VARCHAR };
        }
    
        @Override
        public PhoneNumber fromStringValue(String number) throws HibernateException {
            try {
                return PhoneNumberUtil.getInstance().parse(number, "US");
            } catch (NumberParseException e) {
                throw new HibernateException(e);
            }
        }
    
        @Override
        public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor arg2, Object owner) throws HibernateException, SQLException {
            final String number = rs.getString(names[0]);
            if (number == null) {
                return null;
            }
            return TGPhoneUtils.parsePhoneNumber(number);
        }
    
        @Override
        public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor si) throws HibernateException, SQLException {
            if (value == null) {
                st.setNull(index, Types.VARCHAR);
                return;
            }
            st.setString(index, TGPhoneUtils.formatPhoneNumber((PhoneNumber)value));
        }
    
    
    }
    

    然后这里是辅助类

    import com.google.i18n.phonenumbers.PhoneNumberUtil;
    import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
    import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
    
    public class TGPhoneUtils {
        public static PhoneNumber parsePhoneNumber(String phoneNum) {
            if (phoneNum == null) {
                return null;
            }
            try {
                return PhoneNumberUtil.getInstance().parse(phoneNum, "US");
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static String formatPhoneNumber(PhoneNumber phoneNum) {
            if (phoneNum == null) {
                return null;
            }
            return PhoneNumberUtil.getInstance().format(phoneNum, PhoneNumberFormat.E164);
        }
    }
    

    【讨论】:

      【解决方案4】:

      您可以像这样注释电话号码:

      @ManyToOne
      @JoinColumn(name = "PHONE_NUMBER")
      private PhoneNumber phone;
      

      假设 PHONE_NUMBER 列存在并映射到电话号码的 id。 PhoneNumber 类也需要注释。这假设您可能希望在不同实体之间共享一个电话号码(多对一)。

      关于文件,您可能需要决定是否要将文件数据实际存储在数据库中(通常不是一个好主意)。否则你可以只存储一个带有文件路径的字符串。

      【讨论】:

      • 问题是PhoneNumber 不是我的班级。这是谷歌的。我可以查看它的内部,因为它是开源的,但是,如何处理“黑盒”对象?
      • PhoneNumber 有没有 ID 或者什么可以参考?
      • 您可以尝试自定义用户类型(这是深入的休眠领域,请谨慎行事)。我从来没有写过,但这里有一些文档:i-proving.ca/space/Technologies/Hibernate/…blog.xebia.com/2009/11/09/…
      猜你喜欢
      • 2014-06-02
      • 1970-01-01
      • 2012-03-22
      • 2015-02-08
      • 1970-01-01
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      • 2016-09-20
      相关资源
      最近更新 更多