今天继续昨天的内容,JPA实现一个自定义类型,昨天我们说了怎么实现一个基本类型,今天学习一个复杂一点的。
我们的用例场景是将一个领域对象映射到一个类型,比如有一个PhoneNumber
的对象,它包含三个字段分别是countryCode,cityCode,number
,现在需要将这个对象映射到数据库,我们看怎么来实现:
第一步,创建一个PhoneNumber
类
public class PhoneNumber {
private Integer countryCode;
private Integer cityCode;
private Integer number;
public PhoneNumber(){}
public PhoneNumber(Integer countryCode,
Integer cityCode,
Integer number){
this.countryCode = countryCode;
this.cityCode = cityCode;
this.number = number;
}
public Integer getCountryCode() {
return countryCode;
}
public void setCountryCode(Integer countryCode) {
this.countryCode = countryCode;
}
public Integer getCityCode() {
return cityCode;
}
public void setCityCode(Integer cityCode) {
this.cityCode = cityCode;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}
第二步,创建一个PhoneNumberType
类
public class PhoneNumberType implements UserType {
@Override
public int[] sqlTypes() {
return new int[]{Types.INTEGER, Types.INTEGER, Types.INTEGER};
}
@Override
public boolean equals(Object o, Object o1) throws HibernateException {
return false;
}
@Override
public int hashCode(Object o) throws HibernateException {
return 0;
}
@Override
public Object nullSafeGet(ResultSet resultSet, String[] names,
SharedSessionContractImplementor sharedSessionContractImplementor,
Object o) throws HibernateException, SQLException {
if (resultSet.wasNull())
return null;
int countryCode = resultSet.getInt(names[0]);
int cityCode = resultSet.getInt(names[1]);
int number = resultSet.getInt(names[2]);
PhoneNumber employeeNumber = new PhoneNumber(countryCode, cityCode, number);
return employeeNumber;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor sharedSessionContractImplementor) throws HibernateException, SQLException {
if (Objects.isNull(value)) {
st.setNull(index, Types.INTEGER);
st.setNull(index + 1, Types.INTEGER);
st.setNull(index + 2, Types.INTEGER);
} else {
PhoneNumber employeeNumber = (PhoneNumber) value;
st.setInt(index,employeeNumber.getCountryCode());
st.setInt(index+1,employeeNumber.getCityCode());
st.setInt(index+2,employeeNumber.getNumber());
}
}
@Override
public Object deepCopy(Object o) throws HibernateException {
return null;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object o) throws HibernateException {
return null;
}
@Override
public Object assemble(Serializable serializable, Object o) throws HibernateException {
return null;
}
@Override
public Object replace(Object o, Object o1, Object o2) throws HibernateException {
return null;
}
@Override
public Class returnedClass() {
return PhoneNumber.class;
}
}
在这里,重写的 sqlTypes
方法返回字段的 SQL 类型,顺序与它们在我们的 PhoneNumber
类中声明的顺序相同。同样,returnedClass
方法返回我们的 PhoneNumber
Java 类型。
唯一剩下要做的就是实现在 Java 类型和 SQL 类型之间转换的方法,就像我们为 BasicType 所做的那样。
关键就是实现nullSafeSet
和nullSafeGet
两个方法,它的基本原理就是设置PreparedStatement
的值,以及从ResultSet
中取出值来创建Java
对象;
注意,在这里我们实现的是UserType接口,也许您有的疑问是为什么不用@Embeddable 其实都是可以使用的,但是这里用UserType的原因是如果有需要自定义的情况,还是建议您使用UserType,否则还是使用@Embeddable ;
在具体使用的时候,需要这样配置:
@Type(type = "com.jpa.demo.custom.type.PhoneNumberType")
@Columns(columns = { @Column(name = "country_code"),
@Column(name = "city_code"), @Column(name = "number") })
private PhoneNumber employeeNumber;
好啦,欢迎讨论。