Win a copy of Terraform in Action this week in the Cloud forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Tim Cooke
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Liutauras Vilda
Sheriffs:
  • Jeanne Boyarsky
  • Rob Spoor
  • Bear Bibeault
Saloon Keepers:
  • Jesse Silverman
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
Bartenders:
  • Piet Souris
  • Al Hobbs
  • salvin francis

Hibernate: how to map parent table with static data child table?

 
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm trying to figure out how to map an an parent table 'Employee', with an static data child table. (Is 'static data' the right name for this or do you call it:  reference data, list of values, lookup data?)

When I insert an new employee the id record number of the static table data record must be added to the employee table. Nothing needs to be changed on the static data table side (= child table).

Unit is my static data table with the fields: id, unitcode, description.

This is my mapping on the employee side:
In the employee table I have an field 'unit_id' (generated by Hibernate, due to ManyToOne declaration, that's for me an indication that it is till so far good)

On the unit side I have nothing defined.

When I insert an employee record:I get the error: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : nl.nkamp.crud.model.EmployeeModel.unit -> nl.nkamp.crud.model.Unit
But this error indicates if I'm right, that hibernate want to save the data on the side of the child table and it is gone, before it can be saved on the parent side. But I only want to save the unit id on the parent = employee side.


I have tried different FetchTypes.

I hope that someone can point me in the right direction to help me out.
 
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I always called them "constant tables" myself. Or sometimes lookup tables, although you can use that term for tables which do change.

I believe you've done a @ManyToOne when you should have done a @OneToMany. That is, many Employees can reference a single row in the Unit table.
 
Nico van de Kamp
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello Tim,

Thanks. I have changed it to @OneToMany, I still have that bloody error.

There are a lot examples with @ManyToOne, @OneToMany and @ManyToMany, but till now I couldn't find examples with static data.
(Or instead of static data: constant data, reference table, lookup, list of values)
 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Worth noting: I don't generally use Eager Fetch, but when you have a small lookup table, sometimes it's not a bad idea. The child table is just going to keep getting refetched anyway.

As for your main problem. Virtually everyone thinks of ORM as being basically a fancy way to do SQL without the ugly JOINs. But it's not. Object Relational Management is exactly what it says. So if you do a fetch on the parent, the child link for the parent instance is either going to point to an actual child instance or a proxy for the child instance (when it's a lazy fetch). So if you fetch a parent, and don't use the child, you should (I THINK) be able to save just the parent. On the other hand, if you so much as LOOK at the child via the parent, then you'll probably have to persist both parent and child, if not manually then via a cascade. JPA, alas., does not have any annotation I know of for a read-only table. In fact, no DBMS I know of does either. The best you can do is slap a SELECT-only security authorization on the userid that you're accessing under. And JPA doesn't directly see that (although a failed UPDATE is its own detection method!)

Contrariwise, if you persist a child and it has a parent collection (OneToMany), mapping you'd have to add or remove the parent from that collection and persist the child even though the actual child table hadn't changed. Because Hiernate's idea of who's connected to whom HAS changed and needs to be kept up to date.

It's not as bad as it sounds. JPA keeps "dirty record" indicators on its entities and if you attempt to persist an unmodified ("clean") record it doesn't actually do anything to the database. And if you do things right, I believe that you don't actually have to obtain an entire entity if you really only care about its ID. But I'm out of practice on that option.

Be aware that I'm not only out of practice, but sometimes fuzzy or wrong in what I say, so don't be surprised. I've worked with ORM for quite a long time and done some pretty gnarly things, but never descended to the uttermost internals (when I could help it, anyway).
 
Sheriff
Posts: 22511
122
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:I believe you've done a @ManyToOne when you should have done a @OneToMany. That is, many Employees can reference a single row in the Unit table.


If many Employees can be linked to one Unit, then Employee should have @ManyToOne. The first word in these annotations is the word that applies to the class they are in. So @ManyToOne inside class Employee indicates that many Employees can be linked to one Unit.


Regarding the persist: there are no cascades defined. With a cascade of ALL or PERSIST, persisting an Employee would persist its Unit if it wasn't persisted yet. Since there are no cascades, any Unit set on the Employee must exist in the database. It either needs to be persisted before the Employee is persisted, or come from the database. That's because Hibernate must know what value to store in the foreign key field of the Employee table. (Hibernate actually allows you to create a Unit with a valid id set without actually persisting it; I've used that trick before.)

Tim Holloway wrote:JPA, alas., does not have any annotation I know of for a read-only table.


It kind of does, but it's not as straight-forward as one single annotation applied in one location.

@Column has properties insertable and updatable. Both default to true, but setting the latter to false will make JPA skip including it in UPDATE statements. If an entire table needs to be read-only, you'd need to add @Column to every single field.
 
Nico van de Kamp
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello Rob,

Thanks, you describe it completely right:
- Many Employees can be linked to a unit
- The unit table will be filled before the app is executed, it's a reference table (or static data table I don't know how this is exactly called in Engilsh, in dutch it is called 'stam tabel'). This value's wil be used in a dropdown/lookup field at the frondend

Here my code for the Employee side (parent)

And this is code for the unit side:


And this is my postman JSON insert:

And do still have this error:"org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : nl.nkamp.crud.model.EmployeeModel.unit -> nl.nkamp.crud.model.Unit; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : nl.nkamp.crud.model.EmployeeModel.unit -> nl.nkamp.crud.model.Unit"

(What I not understand is, I think this is very common in software working with a reference table. Also for a Many-To-Many situation. But I can't find a good example...)
 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I guess the next question is "are you persisting both employee and unit in a single @Transaction?

A "transient" ORM object is one that has not been persisted to the database. I should also point out that some care has to be observed because certain JPA operations do non-intuitive things. For example, if you re-attach an Entity, the "attach" method doesn't simple mark that Entity as having been attached, it creates a whole new copy of that Entity and returns it. The new copy equals() the original, but it does not "==" it. When a JPA method returns an Entity, always use the returned copy!
 
Rob Spoor
Sheriff
Posts: 22511
122
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Are you sure that an Employee's unit cannot be changed? Perhaps that's where the issue lies, especially if you assign a different unit.
 
Nico van de Kamp
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rob Spoor wrote:Are you sure that an Employee's unit cannot be changed? Perhaps that's where the issue lies, especially if you assign a different unit.



Yes, the unit table itself will not be changed. Of course the FK unit_id value of an Employee can change. If a employee is moved to another unit, than the the FK unit_id value will be changed by updating of the employee. But the Unit entity (table) will not be changed.

The Unit table will be changed when a new Unit exist, than the Unit table will be added by a new record, but that needs to be done independent from employee.
 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
OK, let me explain again.

error message wrote:
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation :
nl.nkamp.crud.model.EmployeeModel.unit -> nl.nkamp.crud.model.Unit



The foreign key reference "unit" in EmployeeModel is pointing to an instance of Unit that is in a Transient state. That is, the Unit instance in question has not yet been saved to the database (or a facsimile has been manually created and has not been linked to the database).

If you wish obtain the Unit when it is already in the database, you must first fetch the actual Unit object from the persistenceManager and inject it into the EmployeeModel instance. You cannot construct one on the fly!

Now you might be thinking that it's an awful lot of trouble to pull the entire Unit instance when you really only want to link to it and you're right. JPA does have, as I recall, a way to retrieve a Unit reference that can serve as a proxy for the Unit.
 
Rob Spoor
Sheriff
Posts: 22511
122
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Nico van de Kamp wrote:

Rob Spoor wrote:Are you sure that an Employee's unit cannot be changed? Perhaps that's where the issue lies, especially if you assign a different unit.



Yes, the unit table itself will not be changed. Of course the FK unit_id value of an Employee can change. If a employee is moved to another unit, than the the FK unit_id value will be changed by updating of the employee. But the Unit entity (table) will not be changed.


You need to remove the insertable = false, updatable = false from the join column, because with them it will be impossible to set the unit.

Tim Holloway wrote:If you wish obtain the Unit when it is already in the database, you must first fetch the actual Unit object from the persistenceManager and inject it into the EmployeeModel instance. You cannot construct one on the fly!


At least some versions of Hibernate allow this, as I've used this in my code. It's important that the primary key is fully populated though (for single field primary keys that's easy, for compound primary keys all fields need to be set).

Nico, can you check that the unit you try to set indeed has its primary key set, and that this primary key exists in the database?
 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rob Spoor wrote:

Tim Holloway wrote:If you wish obtain the Unit when it is already in the database, you must first fetch the actual Unit object from the persistenceManager and inject it into the EmployeeModel instance. You cannot construct one on the fly!


At least some versions of Hibernate allow this, as I've used this in my code. It's important that the primary key is fully populated though (for single field primary keys that's easy, for compound primary keys all fields need to be set).



I don't think so If you have an Employee instance that's in the transient state (just constructed) and a Unit instance that's also transient, JPA wants to persist BOTH of them If persisting the Unit record finds an instance already in the database, it's going to throw an Exception.

If an Employee references a Unit that's in an "Attached" state, no problem. A Unit that's in "detached" state ALSO cannot be used. JPA wants assurances that everything its working with is in a known condition and you cannot give that under the default JPA settings when you reference an object, it's not enough to assume the contents of the object haven't changed in the backing database.

If you attempt to persist an Employee and the Unit instance that Employee references is in the transient state, the result is as we see.

As I said before, ORM is not the same thing as SQL. Entity Objects don't link to other Entity Objects by key value. They link as Object properties (references).
 
Rob Spoor
Sheriff
Posts: 22511
122
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I've checked the code some more, and I think I found out why it works. At work we have a tendency to not use entityManager.persist but entityManager.merge. For entities with no id fields set the effect is mostly the same. However, I think that this is a case where merge works and persist doesn't.

Here's a simplified and anonymized code snippet from the project I referred to:
(That comment is copied as-is.)

So Nico, you probably need to replace your Unit instance with one that comes from the database. A simple entityManager.find(Unit.class, unit.getId()) should get you the entity that represents the same object. You could use entityManager.merge as well, but I never liked doing that for anything but detached entities.
 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rob Spoor wrote:
So Nico, you probably need to replace your Unit instance with one that comes from the database. A simple entityManager.find(Unit.class, unit.getId()) should get you the entity that represents the same object.



That's what I've been saying all along!

EntityManager.persist() is used to INSERT an object into a database. EntityManager.merge() is used to synchronize what's in your memory object and what's in the database.

And note that what merge() returns is not what goes into merge!!! This is essential. As I said, the returned object is "equals()" but NOT "==" and you should use the returned object in further operations, not the original one.

Also .getId() is what I was referring to when I said that there was a way to get a reference to Unit without actually pulling in a copy of Unit.

You guys finally made me go back and read the JavaDocs! Grrr.
 
Rob Spoor
Sheriff
Posts: 22511
122
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:EntityManager.persist() is used to INSERT an object into a database. EntityManager.merge() is used to synchronize what's in your memory object and what's in the database.


I know, that's why I'm not a fan of using merge for anything but saving detached entities. I prefer using persist for inserts, some people at work don't agree with me though.

And note that what merge() returns is not what goes into merge!!! This is essential. As I said, the returned object is "equals()" but NOT "==" and you should use the returned object in further operations, not the original one.


I'm aware of that. That's why we return the result of the merge operation, not the argument to it.

You guys finally made me go back and read the JavaDocs! Grrr.


Sorry about that...
 
Nico van de Kamp
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello Tim and Rob,

Thanks for you're help.
Slowly I realize what is going on behind the mappings as @ManyToOne etc.
My mindset:
When I have declared the @ManyToOne annotion (using: spring.jpa.hibernate.ddl-auto=create-drop), the field "unit_id" is 'automatic' created by Hibernate.
I thought thats smart, Hibernate knows that for the emloyee instance only the FK 'unit_id' is needed. By returning the value of the dropdown (=unit id ), of the frontend (postman) will be enough. (At least with PHP is this enough.)
Another reason why I didn't think about that I do not need the unit instance, because I do want only to save/update the employee instance, not more not less

As Tim already said

As I said before, ORM is not the same thing as SQL. Entity Objects don't link to other Entity Objects by key value. They link as Object properties (references).


I have under estimate this, I realize now.

So I understand now a complete unit instance is necessary and this working now!

The weekend I was busy again and I discovered the persistence manager you also mentioned.
To learn myself if the frontend only returns the value (= id) of the dropdown, than I will get the unit instance by id.

First I saw and have added an persistence.xml (which I think is not really necessary, because I'm not using 'EntityManagerFactory'):

This is what I have defined in my Employee (model)

And here is the code of my serviceimpl

And this is what in inject with Postman:

This is working for me now so far. I don't know if someone of you says, this is a good at least this is the direction which I have to go.

One more to go, an Many-To-Many with a join table and also static (or reference) data.

Tim and Rob thanks a lot for you're assistence both!
 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Nico van de Kamp wrote:
So I understand now a complete unit instance is necessary and this working now!


Almost. A good fake (proxy) is OK, too - as long as all you need to do is reference the key and not the unit's other properties. That's what the get ID method does for you.

If you're not using an Entitymanager, then who's doing the persisting? Is this an EJB3 container?

Anyway, good luck on the Many-to-Many. I find that one to be the most challenging of all!
 
Rob Spoor
Sheriff
Posts: 22511
122
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Nico van de Kamp wrote:persistence.xml (which I think is not really necessary, because I'm not using 'EntityManagerFactory')


You're using an EntityManager. That means that you're also using an EntityManagerFactory, because that's providing the EntityManager. It's just hidden from you.
 
Nico van de Kamp
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

If you're not using an Entitymanager, then who's doing the persisting? Is this an EJB3 container?

I do not understand the question sorry, I think due a lack of knowledge of my.

Anyway, good luck on the Many-to-Many. I find that one to be the most challenging of all!



I have the many-to-many also get to work, with a static data table. I think the solution for this "cascade = CascadeType.MERGE" or not? If I use 'CascadeType.ALL',  new records are added to the static data table, with a same value. With MERGE nothing is added and/or changed at the static data table.

I have some other questions, but I will put this in another thread.

 
Tim Holloway
Saloon Keeper
Posts: 24587
168
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Actually, now I'm confused. You have defined EntityManagerFactory properties to a single persistence service class, if I read that properly. In a typical app, you'd have lots of service classes, and so you'd probably end up having to repeat the factory property list over and over (and possibly end up with resource issues).

In fact, I run a 2-tier system myself. The Persistence Service classes handle "working sets" of data, and they get their Entities from my DAO tier. Both tiers run under the same @Transaction (DAO's inheriting from the Service), but only the DAO's have EntityManagers injected into them.

The reason I do this is that Persistence Services often contain business logic and juggle several different types of connected Entities which I can treat as a unit (graph, if you like). The DAOs, on the other hand are one-table-per-DAO, except occasionally where parent-child connections are especially strong.
 
You showed up just in time for the waffles! And this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
reply
    Bookmark Topic Watch Topic
  • New Topic