Using UUID with EclipseLink and PostgreSQL - java

I want to use the PostgreSQL uuid type for objects' primary keys.
For that I've created a converter (implementing the Converter interface).
Bellow is the relevant code:
#Override
public void initialize(DatabaseMapping mapping, Session session) {
final DatabaseField field;
if (mapping instanceof DirectCollectionMapping) {
field = ((DirectCollectionMapping) mapping).getDirectField();
} else {
field = mapping.getField();
}
field.setSqlType(Types.OTHER);
field.setTypeName("uuid");
field.setColumnDefinition("UUID");
}
Then I've annotated the relevant entity X with the bellow annotations:
#Converter(name="uuidConverter",converterCalss=UUIDConverter.class)
#Convert("uuidConverter")
#Id
public UUID getId()
{
return id;
}
The problem is that I have another class (Y) which has the following definition:
#ManyToOne(targetEntity = X.class)
#JoinColumn(name = "x_id")
public X getX();
Although EclipseLink created the tables as expected it sends a string to the database when trying to insert objects of type Y.
Postgres returns the following error message:
column "id" is of type uuid but expression is of type character varying at character
Any solutions / work around will be appreciated.

I had the same issue with EclipseLink JPA + Postgresql + UUID as primary key.
To solve it, I've merged codes from Github and below link:
https://forums.oracle.com/forums/thread.jspa?messageID=4584157
The below code for UUIDConverter worked for me, though the code surely isn't the best.
public void initialize(DatabaseMapping ARGMapping, Session ARGSession)
{
final DatabaseField Field;
if (ARGMapping instanceof DirectCollectionMapping)
{
Field = ((DirectCollectionMapping) ARGMapping).getDirectField();
}
else
{
Field = ARGMapping.getField();
}
Field.setSqlType(Types.OTHER);
Field.setTypeName("uuid");
Field.setColumnDefinition("UUID");
for (DatabaseMapping m : ARGMapping.getDescriptor().getMappings())
{
assert OneToOneMapping.class.isAssignableFrom(ManyToOneMapping.class);
if (m instanceof OneToOneMapping)
{
for (DatabaseField field : ((OneToOneMapping) m).getForeignKeyFields())
{
field.setSqlType(Types.OTHER);
field.setColumnDefinition("UUID");
field.setTypeName("uuid");
}
}
}
}

I had some issues with EclipseLink JPA 2.1 + Postgresql + UUID as primary key but I find out different solution. I adopted AttributeConverter but I faced a problem with EclipseLink implementation that I resolved with this code:
#javax.persistence.Converter(autoApply = true)
public class PostgresUuidConverter implements AttributeConverter<UUID, Object> {
#Override
public Object convertToDatabaseColumn(UUID uuid) {
PostgresUuid object = new PostgresUuid();
object.setType("uuid");
try {
if (uuid == null) {
object.setValue(null);
} else {
object.setValue(uuid.toString());
}
} catch (SQLException e) {
throw new IllegalArgumentException("Error when creating Postgres uuid", e);
}
return object;
}
#Override
public UUID convertToEntityAttribute(Object dbData) {
if (dbData instanceof String) {
return UUID.fromString(dbData.toString());
} else {
return (UUID) dbData;
}
}
}
public class PostgresUuid extends PGobject implements Comparable<Object> {
private static final long serialVersionUID = 1L;
#Override
public int compareTo(Object arg0) {
return 0;
}
}
As I exaplined in detail in this post http://blog-ungarida.rhcloud.com/persisting-uuid-in-postgresql-using-jpa-eclipselink/

Try checking what the fieldClassification of the mapping is in the initialize method. It might be getting String.class somehow, try setting it to Object.class.
or, field.setType(Object.class)

It seems there is a bug/incompatibility between EclipseLink and PostgresQL. If you just use UUID for primary keys you should be okay. But if you have a nullable UUID column, and you try to store null in it, you will get the reported error:
column "whatever" is of type uuid but expression is of type character varying
See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=538138 (log in and vote for it if you have the time!)
That bug report proved very useful to me. Specifically the link to the forum thread at:
https://www.eclipse.org/forums/index.php?t=msg&th=1073632&goto=1719530&#msg_1719530
I tried all sorts of solutions from here on SO, and elsewhere on the web. The only one that seemed to work for me was the one posted by David Wheeler there. Specifically, creating a cast from character varying to uuid in the database.
Note that you have to be user postgres to create the cast:
$ sudo su - postgres
$ psql <your database name>
# drop cast if exists (character varying as uuid);
# create or replace function uuid(_text character varying) returns uuid language sql as 'select uuid_in(_text::cstring)';
# create cast (character varying as uuid) with function uuid(character varying) as assignment;
For completeness here is the rest of what I use (in case it helps)
All my entities (that have a UUID primary key) extend a base class called EntityBase:
package com.example.entity;
import java.io.Serializable;
import java.util.UUID;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.Converter;
import com.example.converter.UUIDTypeConverter;
#MappedSuperclass
#Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class EntityBase implements Serializable, Cloneable
{
private static final long serialVersionUID = 1L;
#Id
#Convert("uuidConverter")
private UUID id;
public EntityBase() {
this.id = UUID.randomUUID();
}
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof EntityBase)) {
return false;
}
EntityBase other = (EntityBase) obj;
return getId().equals(other.getId());
}
public UUID getId()
{
return this.id;
}
public void setId(UUID id)
{
this.id = id;
}
}
The UUID converter class looks like this:
package com.example.converter;
import java.sql.Types;
import java.util.UUID;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
public class UUIDTypeConverter implements Converter
{
#Override
public UUID convertObjectValueToDataValue(Object objectValue, Session session)
{
return (UUID) objectValue;
}
#Override
public UUID convertDataValueToObjectValue(Object dataValue, Session session)
{
return (UUID) dataValue;
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public void initialize(DatabaseMapping mapping, Session session)
{
DatabaseField field = mapping.getField();
field.setSqlType(Types.OTHER);
field.setTypeName("java.util.UUID");
field.setColumnDefinition("UUID");
}
}
If you have entities that have UUID columns that are not primary keys, you can annotate them as follows:
import org.eclipse.persistence.annotations.Convert
import org.eclipse.persistence.annotations.Converter;
#Entity
#Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class BillingEvent extends EntityBase
{
#Convert("uuidConverter")
private UUID entityId;
}
Note that if that entity has other columns that use the standard javax.persistence.convert annotation, you'll need to differentiate the two Convert annotations to avoid a compile error.
For example:
import javax.persistence.Convert;
import org.eclipse.persistence.annotations.Converter;
#Entity
#Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class BillingEvent extends EntityBase
{
#org.eclipse.persistence.annotations.Convert("uuidConverter")
private UUID entityId;
#Convert(converter = JSR310InstantTypeConverter.class)
private Instant createdOn;
}
I hope this saves others some time. Good luck!

Universal UUIDConverter for EclipseLink (not only PostgreSQL)
Code:
import java.nio.ByteBuffer;
import java.util.UUID;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectCollectionMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
public class UUIDConverter implements Converter {
private Boolean isUUIDasByteArray = true;
#Override
public Object convertObjectValueToDataValue(Object objectValue,
Session session) {
if (isUUIDasByteArray) {
UUID uuid = (UUID)objectValue;
if (uuid == null) return null;
byte[] buffer = new byte[16];
ByteBuffer bb = ByteBuffer.wrap(buffer);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return buffer;
}
return objectValue;
}
#Override
public UUID convertDataValueToObjectValue(Object dataValue,
Session session) {
if (isUUIDasByteArray) {
byte[] bytes = (byte[])dataValue;
if (bytes == null) return null;
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
}
return (UUID) dataValue;
}
#Override
public boolean isMutable() {
return true;
}
#Override
public void initialize(DatabaseMapping mapping, Session session) {
final DatabaseField field;
if (mapping instanceof DirectCollectionMapping) {
// handle #ElementCollection...
field = ((DirectCollectionMapping) mapping).getDirectField();
} else {
field = mapping.getField();
}
if (session != null && session.getLogin()!= null && session.getLogin().getPlatform() != null) {
String platform = session.getLogin().getPlatform().getClass().getSimpleName();
if (platform.equals("PostgreSQLPlatform")) {
field.setSqlType(java.sql.Types.OTHER);
field.setTypeName("java.util.UUID");
field.setColumnDefinition("UUID");
isUUIDasByteArray = false;
} else if (platform.equals("H2Platform")) {
field.setColumnDefinition("UUID");
} else if (platform.equals("OraclePlatform")) {
field.setColumnDefinition("RAW(16)");
} else if (platform.equals("MySQLPlatform")) {
field.setColumnDefinition("BINARY(16)");
} else if (platform.equals("SQLServerPlatform")) {
field.setColumnDefinition("UNIQUEIDENTIFIER");
}
}
}
}

You don't need a converted. Use this column definition in the entity. You need to register the uuid extension first. This works with Postgres 10 and Wildfly 10.1
#Column(name = "UUID", nullable=false, insertable = false, columnDefinition="uuid DEFAULT uuid_generate_v4()")
private String uuid;

Related

JPA 2 merge is inserting instead of updating when primary key is UUID

For one of my entities I want to use UUID as primary key instead of a Long. The entity extends AbstractEntityUUID:
#MappedSuperclass
public abstract class AbstractEntityUUID implements Serializable {
private static final long serialVersionUID = 40076842287035126L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
public UUID getId() {
return id;
}
#Override
public int hashCode() {
if (getId() != null) {
return getId().hashCode();
}
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AbstractEntityUUID other = (AbstractEntityUUID) obj;
if (getId() == null || other.getId() == null) {
return false;
}
if (!getId().equals(other.getId())) {
return false;
}
return true;
}
}
The entity itself is:
#Entity
#Table(schema = "schemaName", name = "order")
#DynamicUpdate
public class Order extends AbstractEntityUUID {
private static final long serialVersionUID = 6358231835578038565L;
#Column(precision = 10, scale = 2)
protected BigDecimal itemPrice;
#Temporal(TIMESTAMP)
#Basic(fetch = LAZY)
protected Date purchaseDate;
// other properties and getters & setters ...
}
The persist method works fine:
Order order = new Order();
order.setItemPrice(BigDecimal.ONE);
order = getEM().persist(order);
A new row is created on the database table with the correct info. When there is a merge later on is when the issue occurs:
order.setPurchaseDate(new Date());
order = getEM().merge(order);
The previous row seems to be left unchanged and a new one is created with a new primary key which includes the price and date.
When the primary key is Long with GenerationType.IDENTITY the row is updated correctly instead and it works as expected.
This is run on a WildFly 16 server on an ejb 3 bean. The Hibernate implementation seems to be 5.3.9. The DB is MySQL server 5.7.
It is fine if Java generates the UUID values and I would rather not change that unless it is required to make the entity work with a UUID as primary key.
Are you sure that persist is working as expected when MySql generates the UUID? Is the id of the entity the same as the id inserted into the database?

Join two tables and send as one record

Suppose, that we have such tables:
Table Users
iduser | password
Table Marks
id | iduser | mark | idtest
Table Tests
idtest | title
Query looks like this:
#GET
#Path("/{id}/marks")
#Produces(MediaType.APPLICATION_JSON)
public Object funkcja(#PathParam("id") Integer iduser) {
Query query = em.createQuery("select m,t from Marks m, Tests t where m.idusers=:iduser and m.idtest = t.idtests");
query.setParameter("iduser", id);
List<Object> results = (List<Object>)query.getResultList();
return results;
}
I have entity classes:
Marks , Users, Tests
What I should to do in order to join tables and send JSON type on web service and how to convert JSON to entity class because I would like to show in TableView.
Perhaps there are other simple ways?
Maybe map or JsonObject?
You seem to have multiple questions here; I think you need to break these down into separate questions.
For the "main" question, which is about JPA and how to join the entities, I would do that at the entity level, not at the query level. I.e. I think I would have entity classes like:
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="Tests")
public class Test {
#Id
#Column(name="idtest")
private int id ;
private String title ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Override
public boolean equals(Object other) {
if (other instanceof Test) {
return Objects.equals(title, ((Test)other).title);
} else return false ;
}
#Override
public int hashCode() {
return Objects.hash(title);
}
}
and then the Mark entity can use a #ManyToOne annotation to reference the actual Test object (not its id):
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="Marks")
public class Mark {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id ;
#ManyToOne
#JoinColumn(name="idtest")
private Test test ;
// You probably don't want a reference to User here, as the User class
// encapsulates the password, which you don't want to throw back and
// forward across the network unnecessarily. But if the User class
// instead had a user name etc you wanted, you could use the same
// #ManyToOne technique to reference a User object here if you needed.
#Column(name="iduser")
private int userId ;
private int mark ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public Test getTest() {
return test;
}
public void setTest(Test test) {
this.test = test;
}
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Mark) {
Mark other = (Mark)obj ;
return Objects.equals(userId, other.userId)
&& Objects.equals(test, other.test)
&& mark == other.mark ;
} else return false ;
}
#Override
public int hashCode() {
return Objects.hash(userId, test, mark);
}
}
Now your query looks like
TypedQuery<Mark> query = em.createQuery("select m from Mark m where m.userId=:userid");
query.setParameter("userid", iduser);
List<Mark> results = query.getResultList();
and the Mark instances in the list already have all the data you need:
for (Mark mark : results) {
System.out.println(mark.getTest().getTitle() + ": " + mark.getMark());
}
For the remaining questions:
Assuming you have a server set up with a JAX-RS implementation (e.g. Jersey) the code snippet you showed (modified with the new query) should generate JSON output. (You can use a developer tool such as Firefox REST client to view the JSON output by specifying the appropriate URL and request headers, and viewing the response.)
On the client (JavaFX) side you can use Jersey's client library (or maybe Apache HttpComponent library) to create the web requests easily, and a library such as GSON or Jackson for mapping the JSON content you get back to a Java object for display in the TableView.
I recommend trying this and asking specific questions about the remaining pieces if you get stuck.

Java EE 6, EJB 3.1, JPA 2.0 - error in inserting record in database

I am trying to insert a record in the database (using Java EE 6, EJB 3.1, JPA 2.0). I am getting an error that accountTypeId field is null, but i have set it up as autogenerate. Can anyone please suggest what am I doing wrong?
Following is the create table query:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
account_type_desc varchar(20)
);
Following is the entity class:
EDIT: Updated the entity class as generated by NetBeans which didn't work. I also added #GeneratedValue annotation but still it didn't work.
#Entity
#Table(name = "ACCOUNT_TYPE")
#NamedQueries({
#NamedQuery(name = "AccountType.findAll", query = "SELECT a FROM AccountType a"),
#NamedQuery(name = "AccountType.findByAccountTypeId", query = "SELECT a FROM AccountType a WHERE a.accountTypeId = :accountTypeId"),
#NamedQuery(name = "AccountType.findByAccountTypeDesc", query = "SELECT a FROM AccountType a WHERE a.accountTypeDesc = :accountTypeDesc")})
public class AccountType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) // ADDED THIS LINE
#Basic(optional = false)
#NotNull
#Column(name = "ACCOUNT_TYPE_ID")
private Integer accountTypeId;
#Size(max = 50)
#Column(name = "ACCOUNT_TYPE_DESC")
private String accountTypeDesc;
public AccountType() {
}
public AccountType(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public Integer getAccountTypeId() {
return accountTypeId;
}
public void setAccountTypeId(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public String getAccountTypeDesc() {
return accountTypeDesc;
}
public void setAccountTypeDesc(String accountTypeDesc) {
this.accountTypeDesc = accountTypeDesc;
}
#Override
public int hashCode() {
int hash = 0;
hash += (accountTypeId != null ? accountTypeId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof AccountType)) {
return false;
}
AccountType other = (AccountType) object;
if ((this.accountTypeId == null && other.accountTypeId != null) || (this.accountTypeId != null && !this.accountTypeId.equals(other.accountTypeId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Entities.AccountType[ accountTypeId=" + accountTypeId + " ]";
}
}
Following is the session bean interface:
#Remote
public interface AccountTypeSessionBeanRemote {
public void createAccountType();
public void createAccountType(String accDesc);
}
Following is the session bean implementation class:
#Stateless
public class AccountTypeSessionBean implements AccountTypeSessionBeanRemote {
#PersistenceContext(unitName="ExamplePU")
private EntityManager em;
#Override
public void createAccountType(String accDesc) {
AccountType emp = new AccountType(accDsc);
try {
this.em.persist(emp);
System.out.println("after persist");
} catch(Exception ex) {
System.out.println("ex: " + ex.getMessage());
}
}
}
Following is the Main class:
public class Main {
#EJB
private static AccountTypeSessionBeanRemote accountTypeSessionBean;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
accountTypeSessionBean.createAccountType("test");
}
}
Following is the error:
INFO: ex: Object: Entities.AccountType[ accountTypeId=null ] is not a known entity type.
You are not getting an error because of "accountTypeId field is null". As the error message says, the error occurs because "Entities.AccountType[ accountTypeId=null ] is not a known entity type".
The most likely reason is that AccountType is not annotated with #Entity. This problem is likely solved by adding it. Additionally it makes sense to use
#GeneratedValue(strategy = GenerationType.IDENTITY)
instead of AUTO. Auto means that the provider chooses a strategy based on the capabilities of the target database. According to the table definition it seems clear that the preferred strategy is IDENTITY.
I changed my create table query as following:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY,
account_type_desc varchar(20)
);
Then had to add the following line to the entity class (Netbeans doesn't add that):
#GeneratedValue(strategy = GenerationType.AUTO)

Hibernate annotation object mapping

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.
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. */
}
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.
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);
}
}
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.

In class without a property called "type" I get org.hibernate.PropertyNotFoundException: Could not find a setter for property type in class

Using JPA, and Hibernate as the provideer, I have a class defined like:
#Entity
#Table(name="partyrole")
public class PartyRole extends BaseDateRangeModel {
private static final long serialVersionUID = 1L;
private Party roleFor;
public void setRoleFor(Party roleFor) {
this.roleFor = roleFor;
}
#ManyToOne
public Party getRoleFor() {
return roleFor;
}
}
And I get the error in title of the question. I've tried adding public void setType(Object type) but that doesn't work either. The persistence.xml file is normal.
There are two classes that reference this one, but neither of them attempts to invoke setType either. I'd apprecaite any help!!!!
This happens at deployment time. The stack trace is at the bottom.
The Parent Class:
package com.nsfw.bmp.common.jpa;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.hibernate.validator.AssertTrue;
import org.hibernate.validator.NotNull;
/**
* Several models are date range sensitive, this base class provides that basic
* functionality.
*
* #author jim
*
*/
#MappedSuperclass
public abstract class BaseDateRangeModel extends BasePersistentModel {
private static final long serialVersionUID = 1L;
private Date from;
private Date thru;
/**
* Determines if a model is active. A model is active if now is after or
* equal to from , and thru is either null, or after now, or equal to now.
*/
#Transient
public boolean isActive() {
Date now = new Date();
boolean afterFrom = from.before(now) || from.equals(now);
boolean beforeThru = thru == null || thru.after(now)
|| thru.equals(now);
return afterFrom && beforeThru;
}
#AssertTrue(message = "Dates are not valid the thru date must be empty, or after the fromdate.")
public boolean areDatesValid() {
if (thru == null) {
return true;
} else {
return thru.after(from);
}
}
#Temporal(TemporalType.TIMESTAMP)
#NotNull
#Column(name = "fromDate")
public Date getFrom() {
return from;
}
public void setFrom(Date from) {
this.from = from;
}
#Temporal(TemporalType.TIMESTAMP)
public Date getThru() {
return thru;
}
public void setThru(Date thru) {
this.thru = thru;
}
}
Its parent:
package com.nsfw.bmp.common.jpa;
import java.io.Serializable;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
#MappedSuperclass
public abstract class BasePersistentModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
protected Long id;
protected Long version = 0l;
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BasePersistentModel other = (BasePersistentModel) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
return true;
}
#Id
#GeneratedValue
public Long getId() {
return id;
}
#Version
public Long getVersion() {
return version;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
public void setId(Long id) {
this.id = id;
}
public void setVersion(Long version) {
this.version = version;
}
}
The Party class is fairly large, with a lot of mappings. This is the one asked for:
/**
* #return the actingAs
*/
#OneToMany(mappedBy="roleFor", targetEntity=com.nsfw.bmp.party.entity.association.PartyRole.class)
#OrderBy("from")
public List<PartyRole> getActingAs() {
return actingAs;
}
Here's the stack trace:
Caused by: org.hibernate.PropertyNotFoundException: Could not find a setter for property type in class com.nsfw.bmp.party.entity.association.PartyRole
at org.hibernate.property.BasicPropertyAccessor.createSetter(BasicPropertyAccessor.java:240)
at org.hibernate.property.BasicPropertyAccessor.getSetter(BasicPropertyAccessor.java:233)
at org.hibernate.mapping.Property.getSetter(Property.java:299)
at org.hibernate.tuple.entity.PojoEntityTuplizer.buildPropertySetter(PojoEntityTuplizer.java:272)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.<init>(AbstractEntityTuplizer.java:149)
at org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:76)
at org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping.<init>(EntityEntityModeToTuplizerMapping.java:80)
at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:325)
at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:457)
at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:131)
at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:84)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:261)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1327)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)
Can you post your party class? I have a suspicion that this has to do with your manytoone mapping. Is there a type column in the partyRole table?
You problem might be linked to the BaseDateRangeModel class, because PartyRole extends it.
can you show us that class?
If you're getting this during startup, it means you have a class somewhere referencing PartyRole via inverse relationship, e.g. something along the lines of
#OneToMany(targetEntity=PartyRole.class, inverse=true")
in some other entity. Set hibernate logging level to DEBUG - it should help you to narrow the problem.

Categories