views:

65

answers:

1

I am writing a webservice to maintain a database. I am trying to use JPA (EclipseLink) for the entity classes. However, the database uses natural primary keys and therefore there's potential that an update on the ID fields will fail due to foreign key constraints. Our DBA has provided a function to update the ID fields which will create a new parent record with the updated ID, update the child records to point to the new parent and delete the old parent.

If the ID fields could be updated "normally", I would have a situation like this:

@Entity
@Table(name = "PARENT")
public class Parent implements Serializable
{
    private static final long serialVersionUID = 1L;
    private String parent;
    private String attribute;
    private Set<Child> childs;

    public Parent()
    {
    }

    @Id
    @Column(name = "PARENT")
    public String getParent()
    {
        return this.parent;
    }

    public void setParent(String parent)
    {
        this.parent = parent;
    }

    @Column(name = "ATTRIBUTE")
    public String getAttribute()
    {
        return this.attribute;
    }

    public void setAttribute(String attribute)
    {
        this.attribute = attribute;
    }

    @OneToMany(mappedBy = "parentBean")
    public Set<Child> getChilds()
    {
        return this.childs;
    }

    public void setChilds(Set<Child> childs)
    {
        this.childs = childs;
    }
}

@Entity
@Table(name = "CHILD")
public class Child implements Serializable
{
    private static final long serialVersionUID = 1L;
    private String child;
    private String attribute;
    private Parent parentBean;

    public Child()
    {
    }

    @Id
    @Column(name = "CHILD")
    public String getChild()
    {
        return this.child;
    }

    public void setChild(String child)
    {
        this.child = child;
    }

    @Column(name = "ATTRIBUTE")
    public String getAttribute()
    {
        return this.attribute;
    }

    public void setAttribute(String attribute)
    {
        this.attribute = attribute;
    }

    @ManyToOne
    @JoinColumn(name = "PARENT")
    public Parent getParent()
    {
        return this.parent;
    }

    public void setParent(Parent parent)
    {
        this.parent = parent;
    }
}

I also have a GenericServiceBean class with a method to call functions:

@Stateless
public class GenericServiceBean implements GenericService
{
    @PersistenceContext(unitName = "PersistenceUnit")
    EntityManager em;

    public GenericServiceBean()
    {
        // empty
    }

    @Override
    public <T> T create(T t)
    {
        em.persist(t);
        return t;
    }

    @Override
    public <T> void delete(T t)
    {
        t = em.merge(t);
        em.remove(t);
    }

    @Override
    public <T> T update(T t)
    {
        return em.merge(t);
    }

    @Override
    public <T> T find(Class<T> type, Object id)
    {
        return em.find(type, id);
    }

    . . . 

    @Override
    public String executeStoredFunctionWithNamedArguments(String functionName,
            LinkedHashMap<String, String> namedArguments)
    {
        Session session = JpaHelper.getEntityManager(em).getServerSession();

        StoredFunctionCall functionCall = new StoredFunctionCall();
        functionCall.setProcedureName(functionName);        
        functionCall.setResult("RESULT", String.class);

        for (String key : namedArguments.keySet())
        {
            functionCall.addNamedArgumentValue(key, namedArguments.get(key));
        }

        ValueReadQuery query = new ValueReadQuery();
        query.setCall(functionCall);

        String status = (String)session.executeQuery(query);

        return status;
    }
}

If I set the ID fields to be not editable:

    @Id
    @Column(name = "PARENT", udpatable=false)
    public String getParent()
    {
        return this.parent;
    }

and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?

Another scenario I don't know how to deal with is where I need to update both the ID and another attribute. Should I call the function which updates (and commits) the ID in the database then make calls to set both the ID and attribute via the normal set* methods and then the persistence context will only commit the attribute change?

Perhaps this is a situation where JPA is not appropriate?

Any advice on this is greatly appreciated.

+1  A: 

If I set the ID fields to be not editable (...) and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?

udpatable=false means the column won't be part of the SQL UPDATE statement regardless of what you do at the object level so the Id shouldn't be updated. And I'm also tempted to say that child entities shouldn't be affected, especially since you're not cascading anything.

Another scenario I don't know how to deal with is where I need to update both the ID and another attribute (...)

Well, my understanding is that you'd have to call the function anyway so I would call it first.

Perhaps this is a situation where JPA is not appropriate?

I'm not sure raw SQL would deal better with your situation. Actually, the whole idea of changing primary keys sounds strange if I may.

Pascal Thivent
The primary key is entered by the user and if there's a typo, we need to allow them to correct it. I don't expect it to happen often, but it's possible.
sdoca
@sdoca: I see. But still :)
Pascal Thivent
I agree, I'm used to working with sequences for IDs but I don't have any control over the use of natural primary keys in this schema.
sdoca
@sdoca: I somehow guessed that you couldn't change the model which is why I didn't insist.
Pascal Thivent