Search code examples
javajpaeclipselink

How to do a custom EJB/JPA mapping type?


I want to store a property into the database as a Long, but use the object with helper methods in the code.

However the object type is a custom type I have that has an internal value (a long) that I want to store to the database.

public final class MyBean extends Number implements Serializable, Comparable<MyBean>
{
    private long myValue;

    public MyBean(Long value) { this.myValue = value; }

    // Other helper methods and overrides

    public MyBean valueOf(Long data)
    {
        return new MyBean(data);
    }

    @Override
    public String toString()
    {
        return String.valueOf(myValue);
    }
}

This is how I am using it:

@Entity
@Table(name = "mybeans")
public class MyBean implements Serializable
{
    private static final long serialVersionUID = 1L;

    MyBean myBean;

    @Id
    @Column(name = "mybean", nullable = false)
    public MyBean getMyBean() { return myBean; }
    public void setMyBean(MyBean value) { this.myBean = value; }
}

Deserializing this object calls toString and works fine (jax-rs/jersey). But when I try to pull it out of the database using my EJB, the error I get is:

The object [1,427,148,028,955], of class [class java.lang.Long], could not be converted to [class com.MyBean]

Saving it produced the error

Can't infer the SQL type to use for an instance of com.MyBean. Use setObject() with an explicit Types value to specify the type to use.

Which makes sense.

But what methods can I add in to male the EJB get the long as the value and use the long to set up a new object?

ANSWER:

Making the class @Embeddable and adding the following attributes worked.

@Embedded
@AttributeOverrides({
    @AttributeOverride(name="value", column=@Column(name="mybean"))
})

(I didn't add EmbeddedId because I added a serial primary key id and just made this a column)

The one caveat is that it won't work with dynamic weaving. I had to add

<property name="eclipselink.weaving" value="static"/>

to my persistence.xml


Solution

  • You can try making MyBean an Embeddable to use that as an EmbeddedId, something like this:

    @Embeddable
    public final class MyBean extends Number implements Serializable, Comparable<MyBean> {
    
        private Long myValue;
    
        public MyBean(Long myValue) {
            this.myValue = myValue;
        }
    
        // Other helper methods and overrides
    
        public MyBean valueOf(Long data) {
            return new MyBean(data);
        }
    
        @Override
        public String toString() {
            return String.valueOf(myValue);
        }
    
    }
    

    In your entity, MyBean will be an EmbeddedId and will look like this:

    @Entity
    @Table(name = "mybeans")
    public class MyEntity implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        private MyBean myBean;
    
        @EmbeddedId
        @AttributeOverride(name="myValue", @Column(name="mybean_id"))
        public MyBean getMyBean() {
            return myBean;
        }
    
        public void setMyBean(MyBean myBean) {
            this.myBean = myBean;
        }
    
    }
    

    Adjust MyBean as you need, such as making Transient some attributes.