+3  A: 

Why do you have to set the primary key of the initial object in the sub-objects? With a proper mapping the reference will get set by the JPA application automatically.

So the answer is: do a correct mapping.

If you need a more detailed answer provide a more detailed question. Including:

  • source code of the involved classes
  • source code used to create and persist the instances
  • exceptions experienced
  • information on which jpa implementation you use

Edit, after more details where provided in the question:

I think your embeddable PK should look something like this:

@Embeddable
public class FieldRulePK implements Serializable {
    @Basic(optional = false)
    @Column(name = "IndexTemplateId")
    private Integer indexTemplateId;
    @Basic(optional = false)
    @Column(name = "FieldNumber")
    private Integer fieldNumber;
    @ManyToOne( ... some not so trivial details here ..)
    private SearchRule searchRule;
}

And the searchRule property of your FieldRule should be dropped. The entity reference in the embeddable should result in an id field in the database.

Jens Schauder
Jens, thank you for the reply. I may post some additional details if I can't resolve this issue. However I left off an important detail in the original question which I've edited in. The child object's primary key is a composite that includes the auto generated primary key from the parent.
James McMahon
A: 

Why does the "sub-object" (I think you mean "child") need to have the key to the parent object? If you have a OneToMany on the Parent object and a ManyToOne on the Child object with mappedBy, your child object will already have a foreign key (and a reference to the parent object).

Also, you need to check you cascade in your Parent object OneToMany annotation.

GreenieMeanie
+2  A: 

This is a database design issue, I think. If the FieldRule can be created independently of the SearchRule (in other words, SearchRuleId is not a "not null" field) then you need to not include it in your composite primary key. If SearchRuleId cannot be null, then you just have to save the objects in the right order, which your ORM should handle for you if your mapping is correct.

Rafe
SearchRuleId can not be null. What is the right order here? Do you see anything wrong with the mappings in the question?
James McMahon
Seems like a basic logic problem. How can you then create a FieldRule unless the SearchRule has been created, as your post indicates. If A is dependent on B, then you must create A before B.
Rafe
I guess what I'm saying is you may want to evaluate your application logic and consider whether the data model matches what's actually going on.
Rafe
@Rafe, ok here is what is going on. I don't want to persist SearchRule prior to the creation of the field rules because the action is being done in a web application and I don't want people to navigate away and leave an orphaned search rule in the database.
James McMahon
In that case, though, don't you want to just create them simultaneously? SearchRule right before FlagRule.
Rafe
@Rafe, yeah that is actually what I ended up doing, see my answer below.
James McMahon
+1  A: 

Posting this mostly because I can't leave this complicated of comment... but anyway...

Normally when I look at EmbeddedId type things I see things like from this example of Embeddable keys. Normally I'd expect something like

From ChildPK.java:

@Basic(optional = false)
@Column(name = "ParentId")
private Parent parent;

But here I guess we've got 2 other FKs being made into a composite PK, IndexTemplateId and FieldNumber... and this Parent object's ID is auto-generated using a sequence.

Now I suppose that you must already be persisting the Parent object prior to trying to persist the child object or you must mark the Parent object in child as cascading, that should ensure the id gets populated, the composite keys seem to greatly complicate the problem.

Since this is a new ORM I would suggest that you use a single PK on each table instead of composite ids and simply have FK relations between the tables.

Apologies if I'm not grasping something here, but I'm not quite sure there is enough information here - I would ask for the entire Entity field declarations just to see how you're trying to put this together each of your 3 classes...

Petriborg
Well regardless if I use an auto generated key for the Child table, I am still am going to need to reference the Parent table. So would removing the composite key actually simplify anything?
James McMahon
I think it would - I have a lot of these sort of links in my own stuff and having each table generate its own id and then having FKs all over works pretty well for me.
Petriborg
A: 

Simple answer: don't rely on your persistence layer generating the IDs at the time of persistence. Create the entity IDs at the time you create the objects.

Unless you are coding some specific meaning into your keys (a database anti-pattern), they can be any random, unique value such as a UUID (GUID for the Microsofties).

And here's something to think about when you use your persistence layer to generate the ID/primary key: do you use the entity's primary key in the hashcode or equals method?

If you do use the ID/primary key in the hashcode/equals method then you will break the contract expected of objects when stored in a Java collection. See this Hibernate page for more details.

hbunny
A: 

Right now my work around is doing something like,

Collection<FieldRule> fieldRules = searchRule.getFieldRuleCollection();

if (searchRule.getId() == null)
{
    //null out the collection so it doesn't cascade on persist
    searchRule.setFieldRuleCollection(null);
    //save to get id
    dao.saveSearchRule(searchRule);
    for (FieldRule fr : fieldRules) {
        fr.getFieldRulePK().setSearchRuleId(searchRule.getId());
    }
}
//re set collection
searchRule.setFieldRuleCollection(fieldRules);
//remove double refrence, which jpa doesn't like, to FieldRuleCollection
fieldRules = null;

//save again, this time for real
dao.saveSearchRule(searchRule);

That seems really hackey to me, but it does work (maybe, I'm hitting some other issues but they may be unrelated).

There must be a better way to turn off casacade for a single persist.

James McMahon
+1  A: 

Something is a bit fishy here. Generally speaking if you have parent entity A and child entity B and you are persisting A with some children the correct order of operations is first inserting A into the database and then inserting children (I am assuming proper cascade from A to B). So in this general case the ids will be properly generated and everything should OK.

However it appears that in your case children (FieldRules) are saved first. The only reasonable explanation for this I can think of is that if you have an additional entity C (in your case probably Field entity) which is already saved when your code is running and it has a cascade to FieldRules. In this case you have two conflicting cascades: one SearchRule -> FieldRule and another Field -> FieldRule. Since JPA doesn't perform smart analysis of this it is a matter of chance (and loading order) which one will get invoked first. And in your case the Field->FieldRules is probably invoked which causes the children to be inserted before parent.

So I would try to search for any additional cascades TO FieldRules in your code and try to remove those. If you can remove them all it will probably solve your problem

Gregory Mostizky
@Gregory, very interesting, let me dig around my controller and see what I can find.
James McMahon
@Gregory, I am getting other bits of data from the database at various points, but the only persist I am doing is on the SearchRule.
James McMahon
Just to clarify the issue is not with persist() method. The way JPA works (I am mostly experienced with hibernate but I assume the rest is similar) is that it has a list of "attached" entities internally. When you are persisting() entity it is added to the list. When entity is loaded from DB it is added to the list. So when JPA does the flushing which means it actually goes and runs SQL statements it will go over that list and calculate what it needs to do. And it is entirely possible that the Field entity is already there *before* SearchRule and it 'wins' and does the cascade first.
Gregory Mostizky
@Gregory, what would you recommend as a workaround?
James McMahon
Even when doing cascading, you still have set the parent id on the child object, correct?
James McMahon
I think there are 2 options. One is to just use UUIDs and set them manually and not use the database for ID generation as was suggested somewhere else. Another is to fix all the cascades and make sure the entities are inserted in correct order. In that case you still need to make sure that the child will reuse parent's id.
Gregory Mostizky
Gregory Mostizky
+1  A: 

Bottom line, your searchRule MUST be saved before your fieldRules can be.

However, rather than having the column definition on the field, you could try having it on a getter...

@Embeddable
public class FieldRulePK implements Serializable {
    //snip other columns
    @Basic(optional = false)
    @Column(name = "SearchRuleId")
    private Integer getSearchRuleId()
    {
        return this.fieldRule.searchRule.getId();
    }
    private void setSearchRuleId(Integer id)
    {
        this.fieldRule.searchRule = new SearchRule(id);
    }

This would mean that when the saveSearchRule(searchRule) cascades into the FieldRuleCollection to save that, the searchRuleId is automatically retrieved from the searchRule after it is saved, rather than having to hackily be added in.

It means whatever creates your FieldRulePK object has to pass a reference to it's parent, but otherwise means your hacky setSearchRuleId() loop is unnecessary.

Stobor
disclaimer: I've never used JPA, that's just how I understand it should work...
Stobor
+1  A: 

I think the problem is with the way you're doing your mapping, where you're trying to pull too many database concepts into your OO model. ORM was a little confusing to me as well, when I started doing it. What you need to understand is that the concept of a primary key field is a database concept and not an OO concept. In OO, each object reference is unique, and that's what you use to identify instances.

Object references do not really map well to the database world, and that's why we have primary key properties. With that said, the use of primary key properties should be kept to a minimal. What I find helpful is to minimize the type of primary key properties that map directly to the primary key columns (usually, integer properties that map to a primary key column).

Anyway, based on that, here's how I think you should do your mapping (changes highlighted with horizontal separators):

From FieldRule.java (Child Class):

public class FieldRule implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected FieldRulePK fieldRulePK;
    @Basic(optional = false)
    @Column(name = "RuleValue")
    private String ruleValue;


    // Removed field and searchRule mapping as those are already in the 
    // primary key object, updated setters/getters to pull properties from
    // primary key object
    public Field getField() {
        return fieldRulePK != null ? fieldRulePK.getField() : null;
    }
    public void getField(Field field) {
        // ... parameter validation ...
        if (fieldRulePK == null) fieldRulePK = new FieldRulePK();
        fieldRulePK.setField(field);
    }

    public SearchRule getSearchRule() {
        return fieldRulePK != null ? fieldRulePK.getSearchRule() : null;
    }
    public void setSearchRule(SearchRule searchRule) {
        // ... parameter validation ...
        if (fieldRulePK == null) fieldRulePK = new FieldRulePK();
        fieldRulePK.setSearchRule(searchRule);
    }


From FieldRulePK.java (Child PK Class):

@Embeddable
public class FieldRulePK implements Serializable {


    // Map relationships directly to objects instead of using integer primary keys
    @JoinColumns({@JoinColumn(name = "IndexTemplateId", referencedColumnName = "IndexTemplateId", insertable = false, updatable = false), @JoinColumn(name = "FieldNumber", referencedColumnName = "FieldNumber", insertable = false, updatable = false)})
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private Field field;
    @JoinColumn(name = "SearchRuleId", referencedColumnName = "ID", insertable = false, updatable = false)
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private SearchRule searchRule;


SearchRule.java should be fine as it is.

I hope this all makes sense.

Note that this is untested, it would take too much time for me to set up a test database and create all the necessary test code, but I hope it gives you an idea on how to proceed.

Jack Leow
Thank you for the answer Jack, however I have actually gotten rid of the composite primary keys to see if that simplifies things.
James McMahon
It was really difficult to pick a bounty winner as many of the answers said the same thing, however I went with this answer as I feel it was the most comprehensive. Essentially, I am going to remove the composite keys and focus on where I am doing my cascades, and in some cases, remove the cascades and handle things by hand because it just makes things easier.
James McMahon