Hibernate注解对象映射

发布于 2024-12-14 23:04:06 字数 1321 浏览 1 评论 0原文

我对 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 之前保留为 String,并使用 PhoneNumber 构造函数将其转换为 Phonenumber。

I'm pretty new with hibernate, and I'm trying to transform a JDBC project I have into Hibernate.

I'm using annotations, and I managed to annotate the basic stuff, however, I'm stuck now with the more heavy objects, I don't know how to annotate them.
Here's the Class:

@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;

Edit:
The File was previously mapped as a BLOB.
The PhoneNumber was previously persisted as String, and was transformed using the PhoneNumber constructor to Phonenumber.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

回忆躺在深渊里 2024-12-21 23:04:06

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

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

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

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

要编写 PhoneNumberType,您需要实现 UserType。汇编/反汇编/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. */
}

The other comments about using @Lob are correct for the File type. It is also correct that if you can change the schema to not save the file data in the DB, then you probably should.

To map your PhoneNumber class to a database field, you're going to need to use a Hibernate custom UserType. It basically tells Hibernate HOW to do the object<-->db mapping for classes that it doesn't already know about. Telling the PhoneNumber field in Person to use a custom user type is easy:

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

This assumes a very simple one-column storage of the phone number.

To write PhoneNumberType, you'll need to implement UserType. It looks overwhelming, with the assemble/disassemble/deepCopy, but the main part you care about is nullSetGet/Set, returnedClass and sqlTypes. You'll end up with some code like this inside your custom type:

@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());
}

You can find plenty of information about how to implement the other methods via google, stackoverflow and the hibernate javadocs. It isn't that hard to do.

UPDATE: Multi-column user type

Implement CompositeUserType instead of just UserType. There are a few method changes that you care about. First you'll want to define the multiple property names and types:

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

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

There's also getPropertyValue/setPropertyValue to implement. Your nullSafeXxxx implementations would change to read and write two properties instead of one:

@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. */
}
深爱不及久伴 2024-12-21 23:04:06

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

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

IMO 这通常会造成比其价值更多的麻烦,但这部分取决于数据库、驱动程序版本等。

Personally, I'd store only the filename in the object, and keep the file on the filesystem, where files belong.

Otherwise, map it as a Hibernate blob (@Lob) and you'd want it to be a byte array (would translate to a blob).

IMO this usually creates more trouble than it's worth, but that depends partially on the DB, driver revision, etc.

爱,才寂寞 2024-12-21 23:04:06

只需为 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);
    }
}

Just create a Hibernate UserType for PhoneNumber

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));
    }


}

and then here is the helper class

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);
    }
}
后eg是否自 2024-12-21 23:04:06

您可以这样注释 PhoneNumber:

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

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

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

You can annotate PhoneNumber like this:

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

Assuming that the column PHONE_NUMBER exists and maps to the id of a phone number. The class PhoneNumber will also need to be annotated. This assumes that you want to maybe share a phone number among different entities (Many to one).

Regarding file, you probably need to decide if you want to actually store the file data in the db (normally not a good idea). Otherwise you could just store a String with a path to file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文