Reputation: 28920
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.
Upvotes: 1
Views: 10547
Reputation: 10595
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);
}
}
Upvotes: 1
Reputation: 3707
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. */
}
Upvotes: 6
Reputation: 4380
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.
Upvotes: 0
Reputation: 160291
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.
Upvotes: 1