Generator class "foreign" does not actually generate anything

There seems to be no automated way to pass a parent’s generated ID value into the children of an object where applicable, even when <generator class="foreign"> is specified in the child.

For example:

Say you have the following relation - A company can have a PIA agreement, but is not required to have one. Your hbm xml configuration would look something like this:

Company.hbm.xml

<hibernate-mapping>
    <class name="com.something.something.something.Company" table="COMPANY">
        <id name="id" column="ID">
            <generator class="increment"/>
        </id>

        <property name="name" column="NAME"/>
        
        <one-to-one name="pia" class="com.iti.dexcenter.common.object.Pia" cascade="all-delete-orphan" foreign-key="companyId"/>
    </class>
</hibernate-mapping>

Pia.hbm.xml

<hibernate-mapping>
    <class name="com.iti.dexcenter.common.object.Pia" table="PIA">
        <id name="companyId" column="company_id">
            <generator class="foreign">
                <param name="property">company</param>
            </generator>
        </id>

        <property name="agreementNumber" column="AGREEMENT_NUMBER"/>
        <one-to-one name="company" class="com.something.something.something.Company" />
    </class>
</hibernate-mapping>

Then, when I call .save(company) to add the company entry (and the pia entry if one is provided) to the DB, you get this error regardless of whether a pia is set on the company object:

attempted to assign id from null one-to-one property [com.something.something.something.Pia.company]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.something.something.something.Pia.company]

The issue has been reported by many other people without a proper resolution:

Basically, you need to do this bizarre thing here

    if(company.getPia() != null) {
        company.getPia().setCompany(company);
    }

Before you call .save(), as you can see:

@Override
@Transactional
public int insertCompany(Company company) {
    int result = -1;

    if(company.getPia() != null) {
        company.getPia().setCompany(company);
    }

    try {
        template.save(company);
        result = company.getId();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return result;
}

***If you don’t do those 3 lines before calling .save(), you’ll get the error mentioned in the stackoverflow post. Why is this necessary??? ***

Some more code…

Company.java (POJO)

public class Company {

    private Integer id;
    private String name;
    private Pia pia;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public void setPia(Pia pia) { this.pia = pia; }
    public Pia getPia() {
        return pia;
    }
    
}

Pia.java (POJO)

public class Pia {

    private Integer companyId;
    private String agreementNumber;
    private Company company;

    public Integer getCompanyId() {
        return companyId;
    }
    public void setCompanyId(Integer companyId) {
        this.companyId = companyId;
    }

    public String getAgreementNumber() {
        return agreementNumber;
    }
    public void setAgreementNumber(String agreementNumber) {
        this.agreementNumber = agreementNumber;
    }

    public Company getCompany() {return company;}
    public void setCompany(Company company) { this.company = company; }

}

I think that you might be missing property-ref="company" on <one-to-one name="pia" class="com.something.something.something.Pia" outer-join="true" fetch="join" cascade="all-delete-orphan" lazy="false"/> to make this work. Also see [HHH-12436] - Hibernate JIRA which seems relevant here.

Hi, unfortunately after trying this suggestion I am still needing to call

if(company.getPia() != null) {
    company.getPia().setCompany(company);
}

Or else I still see the error “attempted to assign id from null one-to-one property [com.something.something.something.Pia.company]”.

The above code is needed before calling .save() or I get that error, and also need it before
UPDATE. Actually, for update to work in all cases regardless of a pia already exists or not for a given company, I have to use .merge() and .saveOrUpdate() depending on whether the pia passed is null, so another non-automated / manual step.

@Transactional
@Override
public int updateCompany(Company company) {
    int result = 0;
    try {
        if(company.getPia() != null) {
            // don't ask why this line is needed
            company.getPia().setCompany(company);

            template.saveOrUpdate(company);
        } else {
            // This was the only thing that worked to remove the pia for a company if no pia was passed in the company object.
            template.merge(company);
        }
        result = 1;
    } catch (Exception e) {
        e.printStackTrace();
        logger.error(e);
        logger.error(e.getMessage());
    }
    return result;
}

Also unsure why I need to call .merge(company) to delete the pia from the db if I pass a company object that has pia set to null. Instead of “merge” also tried:

Company thisCompany = findCompanyByCompanyId(company.getId()); template.delete(thisCompany.getPia());

But this gave me the error

Error during managed flush [deleted object would be re-saved by cascade (remove deleted object from associations): [com.something.something.something.Pia#14]]

Hi, unfortunately after trying this suggestion I am still needing to call

Well, you should always maintain both sides of a bidirectional association, but in this particular case, it is Pia#company that is the “owner” of this bidirectional association, so it is that field/attribute which Hibernate uses to determine the foreign key column value. Usually, people define the setters on the inverse side like this to avoid the problem:

public void setPia(Pia pia) {
    this.pia = pia;
    if ( pia != null ) {
        pia.setCompany(this);
    }
}

Also unsure why I need to call .merge(company) to delete the pia from the db if I pass a company object that has pia set to null. Instead of “merge” also tried:

I don’t understand your confusion. You want a Pia entity to be deleted, so you have two options, call EntityManager.remove(pia) or cause some orphan removal through an attribute cascade. Using EntityManager.merge(company) essentially will trigger that orphan removal, because that is how you specified the one-to-one association Company#pia.

Instead of “merge” also tried:

You see this error because when you run that code, you first load a company into the persistence context, which is then managed at this point. Since Company#pia specifies to cascade all operations, the “flush” of the persistence context will eventually make sure that the object graph is synchronized with the database.
Since you now try to delete a Pia object, there is a kind of conflict. Your explicit EntityManager#remove call via template.delete(..) marks the object as removed, but during the “flush” of the persistence context, it would be re-persisted. So what should Hibernate do in this case? It does the sensible thing and tells you about this inconsistency in your object graph. If you want to remove the Pia object, also make sure that you don’t refer to it anymore from managed entities.

1 Like

I think this is starting to make sense! Thank you for your patience in helping me :slight_smile: for some reason I didn’t think that in a 1-to-0or1 relationship you needed to define the relationship on both sides / in both hbm xml files. Then when I added it I got a lot closer.

Your explicit EntityManager#remove call via template.delete(..) marks the object as removed, but during the “flush” of the persistence context, it would be re-persisted.

So inside my @Transactional marked Java method, if I manually fetch an entity from the DB using .find() to get a parent that contains a child, I can’t delete the child (pia) from that managed parent… Can I delete anything from a managed entity, such as the parent itself if there was no foreign key / if the parent was not part of a 1-to-1 relationship? Or is it related to what is inside @Transactional that determines the object graph being in sync / not being in sync?

For example, in the below method, if I were to change the method to deleteCompany(Company company) instead of passing in an ID, would I be able to just do
template.delete(company);,
instead of setting the pia to null in the loaded/managed “thisCompany” then merging? Or would I just need to do

 template.delete(company.getPia()); 
 template.delete(company); 

I think based on what you said, the below code is working because when I manually fetch an entity from the DB using “findCompanyByCompanyId()” in a method that has the word @Transactional in it, the object graph is not in sync, which is why I have to “merge” to delete the Pia entry - I’m merging the object graph with the db basically. Whereas if I pass in a company object with a null Pia, the graphs are synced from the start since it was passed in, so I wouldn’t need to call .merge() to delete the Pia ?

(Delete company method that is working)

@Override
@Transactional
public boolean deleteCompany(int companyId) {
    boolean result = false;
    try {
        Company thisCompany = findCompanyByCompanyId(companyId);
        thisCompany.setPia(null);
        template.merge(thisCompany);

        template.delete(thisCompany);
        result = true;
    } catch (Exception e) {
        result = false;
        e.printStackTrace();
    }
    return result;
}

If you also want to delete the company, then you could just rely on delete cascading. The Company#pia association already defines cascade="all-delete-orphan", so you don’t have to delete Pia explicitly, Hibernate will take care of that.

if I manually fetch an entity from the DB using .find() to get a parent that contains a child, I can’t delete the child (pia) from that managed parent

The problem is that the managed parent is going to be flushed before transaction commit, but since you marked the Pia object as removed, a conflict appears during flushing of the Company/parent, because Company#pia wants to cascade (that’s what you configured), but runs into this conflict. So if you want to delete the Pia separately, you have to call company.setPia(null), so that cascading does not run into a conflict.

So, since you have delete cascading setup, you can just write this:

@Override
@Transactional
public boolean deleteCompany(int companyId) {
    template.delete(findCompanyByCompanyId(companyId));
}

That deleteCompany tweak you suggested worked! looks a lot cleaner too now… Thanks.

Changing the setter in Company.java (as you suggested) did work and now I no longer need those manual 3 lines in there before every call to .save() or .saveOrUpdate()!

public void setPia(Pia pia) {
    this.pia = pia;
    if ( pia != null ) {
        pia.setCompany(this);
    }
}

In the setter, why is there pia.setCompany(this), and not this.pia.setCompany(this)?
I see there is the line this.pia = pia; above that, is it like Javascript how sometimes when you set a variable to another variable it “syncs” them / when you update one it updates the other, unless you use JSON.parse(JSON.stringify(myObj)) to make a “clone without reference”? So similarly when we do pia.setCompany(this), it is also changing this.pia when we do pia.setCompany(this) because because we did this.pia = pia above it?

Also, why don’t I need an “else” on the condition in the setter, something like

if ( pia != null ) {
        pia.setCompany(this);
    } else {
       pia.setCompany(null);
}

So the reason I cannot call this code to delete a pia

Company thisCompany = findCompanyByCompanyId(company.getId()); 
template.delete(thisCompany.getPia());

is because of my cascade setting cascade="all-delete-orphan". It is a bit strange to a new user that this works:

Company thisCompany = findCompanyByCompanyId(company.getId());
thisCompany.setPia(null);
template.delete(thisCompany.getPia());

One would think this would give the same result as doing JUST
template.delete(null),
since if you were to do thisCompany.setPia(null), then thisCompany.getPia() evaluates to null.

But instead if you do JUST
template.delete(null), you get java.lang.IllegalArgumentException: attempt to create delete event with null entity.

HOWEVER,
this works:

Company thisCompany = findCompanyByCompanyId(company.getId());
thisCompany.setPia(null);
template.delete(null);

So it seems what you pass to .delete() doesn’t really affect much? .delete() is basically just doing a commit of the .setPia(null) we did because of the flush you mentioned that it does before update/save/delete, and flush also commits, which lines up/syncs/updates the db row that matches the pk with the CURRENT STATE OF THE JAVA OBJECT? :thinking:

SO I technically could also do:

Company thisCompany = findCompanyByCompanyId(company.getId());
thisCompany.setPia(null);
template.flush();

?

(newly updated updateCompany() method)

@Transactional
@Override
public int updateCompany(Company company) {
    int result = 0;
    try {
        if(company.getPia() != null) {
            template.saveOrUpdate(company);
        } else {
            // Does not work:
            // Company thisCompany = findCompanyByCompanyId(company.getId());
            // template.delete(thisCompany.getPia());
            // (gives error "deleted object would be re-saved by cascade")

            // Works:
            // Company thisCompany = findCompanyByCompanyId(company.getId());
            // thisCompany.setPia(null);
            // template.delete(thisCompany.getPia());

            // Also works:
            // Company thisCompany = findCompanyByCompanyId(company.getId());
            // thisCompany.setPia(null);
            // template.delete(null);

            // Works and is the least code:
            template.merge(company);
        }
        result = 1;
    } catch (Exception e) {
        e.printStackTrace();
        logger.error(e);
        logger.error(e.getMessage());
    }
    return result;
}

In the setter, why is there pia.setCompany(this) , and not this.pia.setCompany(this) ?

Since you set the field pia of the this object to the object the local variable pia refers to, both pia and this.pia resolve to the same object after the this.pia = pia; assignment, so you can use both variants as they do the same.

Also, why don’t I need an “else” on the condition in the setter, something like

You can surely do that. My examples are just that, examples, not the perfect code that you should use 1:1 in your project :wink:

is because of my cascade setting cascade="all-delete-orphan" . It is a bit strange to a new user that this works:

I have no idea what this template is, but it is not part of Hibernate, so I can’t help you with that.

SO I technically could also do:

Yes, now you seem to slowly understand the concept of managed entities. The idea is that you don’t have to tell the ORM “update this” and “delete that”, but instead you can most of the time just change object state of managed entities and the ORM will figure out what commands to send to the database.

I don’t know why you think you need the saveOrUpdate part. This should suffice:

@Transactional
@Override
public int updateCompany(Company company) {
    int result = 0;
    try {
        template.merge(company);
        result = 1;
    } catch (Exception e) {
        e.printStackTrace();
        logger.error(e);
        logger.error(e.getMessage());
    }
    return result;
}
1 Like

Thanks again…

I have no idea what this 'template' is, but it is not part of Hibernate

I have no idea what template is either LOL
I see it is defined here in the parent DAO java class as:
protected HibernateTemplate template;
Then in the child DAO classes like the one where updateCompany/findCompany/deleteCompany are in (one dao file for companies methods, one dao file for users methods, etc.)
we add
extends HBaseDAOImpl
and
public HCompanyDAOImpl(HibernateTemplate template) { this.template = template; }

Not sure what the difference is between HibernateTemplate and a Session is, though. Been wondering for so long why other people’s code looks so different from ours since they just use variables called “session”.

I’m not really sure why I thought an else was needed here, been running on little sleep this week

if ( pia != null ) {
    pia.setCompany(this);
} else {
   pia.setCompany(null);
}

That else condition will guarantee a null pointer exception.


One last thing…

Why might I need this “@JsonIgnore()”

// the annotation prevents a stack overflow error when loading a company with a pia
@JsonIgnore()
private Company company;

In the POJO Pia.java when I call .find()?

When the annotation was not there, I got my first-ever StackOverflow exception in Java and a “Response already committed” message. But I’m just using .find() to load a company, why would it think something was committed? Note: “DataAccessUtils” is some Spring thing, it will throw an “unexpected row count” error if it returns more than 1 row basically. I don’t think that piece of info should affect why Pia.java needs @JsonIgnore, though.

@Override
public Company findCompanyByCompanyId(int companyId) {
    return (Company) DataAccessUtils.singleResult(template.find("from Company where id=?0", companyId));
}

without @JsonIgnore() in Pia.java gives

WARN  [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleHttpMessageNotWritable()] (default task-8) Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.something.something.something.Pia["company"]->com.something.something.something.Company["pia"]->com.something.something.something.Pia["company"]->...
WARN  [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException()] (default task-8) Handling of [org.springframework.http.converter.HttpMessageNotWritableException] resulted in Exception
java.lang.IllegalStateException: UT010019: Response already commited

Snippet from Pia.java:

 public Company getCompany() { return company; }
 public void setCompany(Company company) {
     this.company = company;
 }

Since you seem to be serializing (implicitly) the company to JSON through some HTTP endpoint, you have to tell the JSON serializer (in your case Jackson) to not serialize certain fields/properties (by annotating @JsonIgnore), because doing so would result in a cycle which JSON can’t handle. The stack overflow is what happens when you serialize a Company then go to serialize the Pia and then back to the same Company again, and the exception message actually even tells you that:

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.something.something.something.Pia[“company”]->com.something.something.something.Company[“pia”]->com.something.something.something.Pia[“company”]->…

I thought we were approaching a resolution to the problem, but unfortunately there are still some things I’m confused about, even though it’s “working” now. It feels like it’s barely holding itself together and I’m almost tempted to just make Company and Pia completely separate and just manually set the IDs myself because this is just so much trial and error and this is going to make 0 sense to anyone who tries to pick up this code in the future, in our entire DB we don’t have ANY parent/child relationships that have been automated via Hibernate, we are manually setting the IDs for children using the parent, deleting each child first instead of the constraints cascading, and nothing is consistent. For how complicated this has been, I almost don’t blame them.

I also feel bad I’m still bugging you about this / that we’re still on this. It’s seeming like the “automation” benefits of hibernate with Parent and Child relationships are more trial and error than I’m actually seeing a benefit with. And the error messages are not helping me at all. Half the time while I’ve been working through this, Hibernate has just been throwing “null pointer” exceptions on .save(), .update(), .delete(), .saveOrUpdate(), with 0 explanation why I’m actually getting an error, like they don’t even know why it’s not working, so they’re like “it didn’t work but we don’t know why so here’s a null pointer exception so you know something went wrong”. Although this is not what I’m facing currently I feel like it’s worth mentioning. It doesn’t explain what property is null that shouldn’t be null, and why it shouldn’t be null. It could be ANYTHING in the object, a property in the child, or the parent. This is probably the simplest example of a 1-to-0or1 relationship that can possibly exist, and I’m still struggling hard.


It turns out using just

template.merge(company);

in updateCompany()

does not work, it doesn’t insert the Pia if one did not exist prior for a given company, and in this circumstance gives the same error originally mentioned in the post

 org.springframework.orm.hibernate5.HibernateSystemException: attempted to assign id from null one-to-one property [com.something.something.something.Pia.company]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.something.something.something.Pia.company]

So updateCompany(Company company) needs to be structured as so (but why? why can’t I just use merge?):

 if(company.getPia() != null) {
      template.saveOrUpdate(company);
 } else {
      template.merge(company);
 }

I need this for it to update the way it should in all circumstances (If no pia existed previously, but I passed one in the Company object, then insert the pia.)

I’m still a little confused about the above, and also why in Pia.java I do not need my setter structured similarly to Company.java. If I were to structure it similarly to Company.java, then the 3 commented lines below would be uncommented:

public void setCompany(Company company) {
    // if (company != null) {
    // company.setPia(this);
    // }
    this.company = company;
}

Why is this not necessary, or is it?

I’m also still confused why I need the annotation @JsonIgnore in Pia.java, you mention doing so would result in a cycle which JSON can’t handle but I’m confused what the doing so part is referring to / what is causing the cycle in the first place. I’d like to know what is causing the infinite recursion because I have no code that could be causing in even a remotely obvious manner. I’m simply loading the Company object which contains a Pia using

@Override
public Company findCompanyByCompanyId(int companyId) {
    return (Company) DataAccessUtils.singleResult(template.find("from Company where id=?0", companyId));
}

So updateCompany(Company company) needs to be structured as so (but why? why can’t I just use merge?):

Because you didn’t setup the bidirectionality in the object graph i.e. Pia#company is null, just like the error says.

Why is this not necessary, or is it?

If you would do that, you would run into a stack overflow error because you’d recursively call the setters of the other which would go in circles.

I’d like to know what is causing the infinite recursion because I have no code that could be causing in even a remotely obvious manner. I’m simply loading the Company object which contains a Pia using

Returning an object from your Spring WebMvc HTTP endpoint method will lead to serialization of this object. Depending on the resulting mime type, either to JSON or XML or whatever.

Don’t get me wrong, but I think you need some serious training or lot of learning from books as you seem to lack the very basics. This is a forum for questions about Hibernate, but your posts/questions more and more go into the very fundamentals of programming and the HTTP stack of Spring.
So I’d like to ask you kindly to ask these questions elsewhere. You’re welcome to ask targeted questions about Hibernate in a new topic if something pops up.