Hibernate: Make a ManyToOne relationship work with unmatched key - java

I have a many-to-one relationship between two objects: SomeProjectType and Work Orders. In SomeProjectType, I have:
#OneToMany(mappedBy = "project", fetch = FetchType.EAGER)
private Set<WorkOrder> workOrders;
SomeProjectType has a "ProjectKey" as the #id for it.
And in WorkOrder I have:
#ManyToOne
#JoinColumn(name = "WorkOrderProjectKey")
private SomeProjectType project;
The issue I am having is that sometimes in WorkOrder, the "WorkOrderProjectKey" has a project key that doesn't exist in SomeProjectType (I am not sure why, but it is by design).
My question is: Is there a way to have Hibernate still return back rows even if some do not match? I have tried "nullable=true" and "optional=true" but it still won't work.

try to this code because i have same problem then i will change code and work properly.
Primary Key Tables
#OneToMany(mappedBy = "project")
private List<WorkOrder> workOrders;
Foreign Key Table
#ManyToOne
#JoinColumn(name = "WorkOrderProjectKey")
private SomeProjectType project;

I got it to work! Under the #ManyToOne, I put the following and it gets everything.
#NotFound( action = NotFoundAction.IGNORE )
Got this from the answer here:
Hibernate chokes on missing rows when dealing with a legacy database

Related

ORA-02292: integrity constraint (xxx) violated - child record found

How do i delete all the entries using hibernate deleteAll() ?
I have a class with multiple #oneToMany relationships (having like +5000 child entities) and when i try to do deleteAll i get the title error
oracle.jdbc.OracleDatabaseException: ORA-02292: integrity constraint (xxx) violated - child record found
I've tried adding
cascade = {CascadeType.ALL}
and
orphanRemoval=true
to #OneToMany relationship class, but no help.
It's a bidirectional relationship with following classes
#OneToMany(targetEntity = XXX.class, fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval=true, mappedBy = "zzz")
#Fetch(FetchMode.SELECT)
#JsonManagedReference
private List<XXX> xxx;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(targetEntity = YYY.class, fetch = FetchType.LAZY, orphanRemoval=true, cascade = {CascadeType.ALL}, mappedBy = "zzz")
#Fetch(FetchMode.SELECT)
#JsonManagedReference
private List<YYY> yyy;
with child elements like
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinColumn(name = "XXX", nullable=false)
#JsonBackReference
private XXX zzz;
i also tried HQL DELETE query but that dosent get me anywhere either.
How on earth do i delete all these entities consistently?
So far i've manually droped the tables since this problem started (all entities were deleted fine just few days ago) but thats starting to really annoy me, but i cant figure how to do this.
Thanks!
You have set CascadeType.ALL on your parent and the best way to delete should be call one single delete on parent entity
If you try to delete a child, it can be hibernate will propagate delete on a parent that has still children not still deleted.
Last resort with this king of problem is:
Enable logs on Spring Boot Application
Run sql query generated in SQL server
Find where the error happens evaluating the current database condition
Change JPA if necessary

Prevent entity deletion if related entity exists

I'm working with JPA 2 + Hibernate 4 and I'm implementing some CRUD operations on model entities.
Now I need to prevent a certain entity (EntityB) to be deleted when a related entity (EntityA) exists in database:
#Entity
public class EntityA {
#Id
private int id;
#OneToOne(mappedBy = "entityA", optional = false, fetch = FetchType.LAZY)
private EntityB entityB;
//...
}
#Entity
public class EntityB {
#Id
private int id;
#OneToOne
#JoinColumn(name = "id")
private EntityA entityA;
//...
}
Is there any way to achieve this using relationship options or should I check EntityA existence in my dao/repository before removing EntityB?
NOTE I need this also for #ManyToOne relationships.
If you want to prevent that in your code, than simply do not delete that entity (by checking that manually). There is no possibility to do that with annotations.
On the other side, this sounds to me rather like a need for a DB constraint. If those entities are already related, then simply add a foreign key constraint (if none is existent). If not, than think of adding one.
PS: if you already have a relationship, check the CascadeType.REMOVE setting.
I don't think you can solve this with annotations. You should manally check related-entity existence before.

Hibernate JPA - ManyToOne relationship not populated

I'm currently moving a (working) app from using EclipseLink to Hibernate JPA, mostly it's gone quite smoothly, but I'm finding one thing that I can't explain, and also can't think of any good search terms!
Basically, I have four entities, with one-to-many relationships forming a chain:
EntityA has a list of EntityB's, each of which has a list of EntityC's, each of which have a list of EntityD's
each of those then has a many-to-one relationship going the other way, so:
EntityD has an EntityC, which has an EntityB, which has an EntityA.
That is (heavily reduced for clarity):
#Entity
public class EntityA {
#OneToMany (cascade = CascadeType.All, mappedBy = "entityA")
private List<EntityB> entityBList;
...
}
#Entity
public class EntityB {
#OneToMany (cascade = CascadeType.All, mappedBy = "entityB")
private List<EntityC> entityCList;
#JoinColumn (name = "ENTITY_A", referencedColumnName = "ENTITY_A_ID")
#ManyToOne (cascade = CascadeType.PERSIST, optional = false)
private EntityA entityA;
}
#Entity
public class EntityC {
#OneToMany (cascade = CascadeType.ALL, mappedBy = "entityC")
private List<EntityD> entityDList;
#JoinColumn (name = "ENTITY_B", referencedColumnName = "ENTITY_B_ID")
#ManyToOne (cascade = CascadeType.PERSIST, optional = false)
private EntityB entityB;
}
#Entity
public class EntityD {
#JoinColumn (name = "ENTITY_C", referencedColumnName = "ENTITY_C_ID")
#ManyToOne (cascade = CascadeType.PERSIST, optional = false)
private EntityC entityC;
}
I get an EntityA from the database (looking it up by its primary key), and thus get a nicely populated EntityA instance, with a PersistentBag for my List<EntityB>. I see a lazy load happening when I dereference that List<EntityB>, and the same repeated for getting EntityCs from EntityB.
At this point, everything is as I expect, I have an EntityA, B and C all fully populated with the values from the database, but then I try to get my EntityD, from EntityC, and find that it's null.
My entity manager is still open and active at this point, and even if I look at it in the debugger immediately after getting the EntityA, I can walk through the relationships, as far as EntityC, and again see the 'entityDList' as null.
The only solution I've found so far is to use:
EntityManager.refresh(entityC);
which populates all its elements including a lazily-loaded PersistentBag for the entityDList.
So, my guess is that Hibernate is only populating the references 2 levels deep (or 3, depending on how you count), and giving up after that, although I don't really understand why that would be. Does that make sense to anyone?
Is there any solution other than the .refresh? Some kind of config or annotation value that will make Hibernate populate the references all the way down?
Thanks to the suggestions from people here, which are probably relevant, but didn't help my specific case.
If you're reading this experiencing the same problem, it's probably worth trying the max_fetch_depth suggestion, but for some reason it didn't work for me (I'd love suggestions as to why?).
Likewise, if your #OneToManys are Sets, rather than Lists, doing an eager fetch or a left join, as suggested by Albert might work, but apparently Hibernate only lets you have a maximum of 1 List that is eagerly fetched, if you need more than that, your collections should be Sets. I didn't try it, but I suspect that it might have solved the problem.
Unless anyone has a better suggestion, I'll stick with calling refresh, which actually probably makes more sense for my application anyway.
This is funny indeed. One way to work around it would be to query object A left join-fetching to ->B->C->D which is also faster if your going to traverse down to object D anyway.
It would be something like this.
"from A left join fetch B left join fetch C left join fetch D"
Have you also tried making the relationship from C->D eager? Curious what will happen then...
The hibernate docs say that you can set it with the hibernate.max_fetch_depth property. The default is 3. You can find it in the "Hibernate Reference Documentation" on page 47.

JPA Cascading Delete: Setting child FK to NULL on a NOT NULL column

I have two tables: t_promo_program and t_promo_program_param.
They are represented by the following JPA entities:
#Entity
#Table(name = "t_promo_program")
public class PromoProgram {
#Id
#Column(name = "promo_program_id")
private Long id;
#OneToMany(cascade = {CascadeType.REMOVE})
#JoinColumn(name = "promo_program_id")
private List<PromoProgramParam> params;
}
#Entity
#Table(name = "t_promo_program_param")
public class PromoProgramParam {
#Id
#Column(name = "promo_program_param_id")
private Long id;
//#NotNull // This is a Hibernate annotation so that my test db gets created with the NOT NULL attribute, I'm not married to this annotation.
#ManyToOne
#JoinColumn(name = "PROMO_PROGRAM_ID", referencedColumnName = "promo_program_id")
private PromoProgram promoProgram;
}
When I delete a PromoProgram, Hibernate hits my database with:
update
T_PROMO_PROGRAM_PARAM
set
promo_program_id=null
where
promo_program_id=?
delete
from
t_promo_program
where
promo_program_id=?
and last_change=?
I'm at a loss for where to start looking for the source of the problem.
Oh crud, it was a missing "mappedBy" field in PromoProgram.
Double-check whether you're maintaining bidirectional association consistency. That is; make sure that all PromoProgramParam entities that link to a PromoProgram as its parent are also contained in said parent's params list. It's a good idea to make sure this happens regardless of which side "initiates" the association if you will; if setPromoProgram is called on a PromoProgramParam, have the setter automatically add itself to the PromoProgram's params list. Vice versa, when calling addPromoProgramParam on a PromoProgram, have it set itself as the param's parent.
I've encountered this problem before as well, and it was due to not maintaining bidirectional consistency. I debugged around into Hibernate and found that it was unable to cascade the delete operation to the children because they weren't in the list. However, they most certainly were present in the database, and caused FK exceptions as Hibernate tried to delete only the parent without first deleting its children (which you've likely also encountered with the #NonNull in place).
FYI, I believe the proper "EJB 3.0"-way of making the PromoProgramParam.promoProgram field (say that a 100 times) non-nullable is to set the optional=false attribute on the #ManyToOne annotation.

One-to-Many Unidirectional Parent-Child ID Cascade Save

When trying to save an ID from my parent class into a child class, I keep getting the error
"ERROR - Field 'parent_id' doesn't have a default value"
I have tried all types of mappings. I am using annotations.
Any help on this would be appreciated
Parent:
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="description")
private String description;
#OneToMany
#Cascade(value= {org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE})
#JoinColumn(name="parent_id")
private List<Child> children;
Child:
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="description")
private String description;
Thanks.
A late addition in case anyone ever runs into the same issue.
This entity here, when persisted using Hibernate 4.1.8, will cascade the FieldChangeentities, but will not fill the join column:
#Entity
public class Event {
//ID and other fields here
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "event_id")
private List<FieldChange<?>> fields = new ArrayList<FieldChange<?>>();
}
Neither does the insert statement set the event_id column, nor does it update the inserted entity after the fact - the event_id remains null and the relation is lost.
If, however, the #JoinColumn definition is changed like this:
#JoinColumn(name = "event_id", nullable = false)
, then the insert statement includes the event_id column like it should, and all is well.
This may only be a regression in this particular version of Hibernate, but maybe it helps someone.
In your case JPA provider to persist child object with its parent perform at least three queries on db. First two persist the objects by its own. The last one
update child object with the foreign key referencing parent. The second query fail because you have a NOT NULL constraint on the foreign key column. You have three options:
Remove NOT NULL constraint on foreign key in the child entity
Use bidirectional relationship
Change JPA provider to one which supports such cases.
You must have something wrong somewhere else because those mappings will work the way they are. They could be better, but they'll work. Specifically, all the #Column annotations are redundant and unnecessary, and as non sequitor noted, you should use the cascade property of JPA's #OneToMany instead of Hibernate's #Cascade. I've created a runnable example with the cleaned-up version of what you posted. If you have git and maven, you can run it with:
git clone git://github.com/zzantozz/testbed tmp
cd tmp
mvn -q compile exec:java \
-Dexec.mainClass=rds.hibernate.UnidirectionalManyToOneJoinColumn \
-pl hibernate-unidirectional-one-to-many-with-join-column
It creates a parent with two children, saves them, and then loads them and prints out the graph. The output is:
Creating parent with two children
Loading saved parent
Parent{description='parent', children=[Child{description='child 2'}, Child{description='child 1'}]}
Change your #OneToMany to #OneToMany(cascade=CascadeType.ALL) use JPA rather than the Hibernate extensions
My guess is that the #JoinColumn annotation needs a referencedColumnName assigned.
#JoinColumn(name = "parent_id", referencedColumnName = "id")

Categories