I would like to associate 2 entities using hibernate annotations with a custom join clause. The clause is on the usual FK/PK equality, but also where the FK is null. In SQL this would be something like:
join b on a.id = b.a_id or b.a_id is null
From what I have read I should use the #WhereJoinTable annotation on the owner entity, but I'm puzzled about how I specify this condition...especially the first part of it - referring to the joining entity's id.
Does anyone have an example?
Here's an example using the standard parent/child paradigm that I think should work using the basic #Where annotation.
public class A {
...
#ManyToOne(fetch = FetchType.EAGER) // EAGER forces outer join
#JoinColumn(name = "a_id")
#Where(clause = "a_id = id or a_id is null") // "id" is A's PK... modify as needed
public B getB() { return b; }
}
public class B {
...
#OneToMany(mappedBy = "b")
public List<A> getA() { return a; }
}
Related
I want to create a Lazy One-to-one Bidirectional 'Optional' Mapping using Hibernate annotations. I know that the normal usage of #MappedBy and #JoinColumn result in N+1 queries being fired every time.
Is there a way I can avoid this? Not just at runtime, but at the POJO level. I am using Hibernate 4.3, so can't think about bytecode enhancement.
Further, if there is no way out, is it possible to apply criteria on unidirectional mappings. For example, I have A <-> B, and C -> A as mappings. And I am searching on B. Is it possible to apply a restriction on C when C is clearly unidirectional with A?
The #OneToOne annotaion doesn't work in hibernate as needed. Please consider the #LazyToOne or try using #OneToMany like #OneToOne. Also you can attempt #PrimaryKeyJoinColumn.
p.s. The #LazyToOne annotation doesn't exist in JPA realization, you should use #OneToOne(fetch = FetchType.LAZY, optional = false) there
I could not find a complete but minimal examples of LAZY bidirectional #OneToOne, so here it is. It is neither hibernate-version-dependent nor does it misuse #OneToMany.
Parent
Defines the id and is responsible for managing the consistency/synchronization, but technically does not own the relationship, because it can not reference any unique index in B (or at least we do not want to add redundant data).
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(
mappedBy = "a",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private B b;
public void setB(B b) {
if (b == null) {
if (this.b != null) {
this.b.setA(null);
}
} else {
b.setA(this);
}
this.b = b;
}
// ... other setters/getters
}
Child
Technically owns the relationship by re-using the id of parent A.
#Entity
public class B {
#Id
// Not generated but shared with A
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "id") // otherwise use "a_id" instead of "id" in the DB column
private A a;
// ... other setters/getters
}
And this is how the tables should look like (assuming postgres):
CREATE TABLE a (
id bigint NOT NULL PRIMARY KEY,
);
CREATE TABLE b (
id bigint NOT NULL PRIMARY KEY,
FOREIGN KEY (id) REFERENCES a(id);
);
I have a Class A which has an object of Class B which has an object of Class C. I want to get object of class B from object of class A without getting object of class C in b. (I have , and want to keep it this way, everything with lazy loading)
I am doing:
Hibernate.initialize(a.getObjectOfClassB());
But get exception. Is there any way to do what i want? Cutting the hibernate initialize chain?
Thanks in advanced!
So your entity structures appear to be mapped as follows:
public class EntityA {
#OneToMany(mappedBy = "a")
private List<EntityB> bList;
}
public class EntityB {
#ManyToOne
private EntityA a;
#OneToMany(mappedBy = "b")
private List<EntityC> cList;
}
public class EntityC {
#ManyToOne
private EntityB b;
}
So you have a specific EntityA that you want to fetch it's associated EntityB instances. You can obtain that list either at query time or as a post initialization step.
The important thing to note here is that the mappings between A - B - C are using #OneToMany which are lazily fetched by default.
To do this at query time:
SELECT a
FROM EntityA a
JOIN FETCH EntityB b
WHERE a.id = :entityAId
The returned EntityA already has your List<EntityB> already loaded for you and you need to do nothing else.
To do this as a post initialization step after you've fetched a single EntityA instance.
Hibernate.initialize(entityA.getBList());
or
entityA.getBList().size();
I've got a many-to-one mapping between two entities (A and B, one B can be associated with many As) where I need the ability to have an ID for B on A (A.B_ID) where that particular B entity doesn't exist in the database. Is this possible?
A (simplified) example of our code:
#Entity
#Table(name = "A")
public class A implements java.io.Serializable {
// ...
private B b;
// ...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "B_ID")
public B getB() {
return this.b;
}
// ...
}
#Entity
#Table(name = "B")
public class B implements java.io.Serializable {
// ...
private Set<A> as = new HashSet<A>( 0 );
// ...
#OneToMany( fetch = FetchType.LAZY, mappedBy = "b" )
public Set<A> getAs() {
return this.as;
}
// ...
}
This basic setup ends up with Hibernate trying to save a null value for A.B_ID and that's not allowed:
Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("MY_SCHEMA"."A"."B_ID")
For clarification, if the entity doesn't already exist, I don't want it to be created. I'd just want A to be inserted with no B in the db. There is no foreign key constraint between the two tables.
I use a #NotFound annotation on the #ManyToOne side to make sure that it won't causes any errors. I haven't tried it yet with a bidirectional relationship though.
Please not that this is a Hibernate specific annotation!
Example:
#NotFound(action=NotFoundAction.IGNORE)
I have the following classes:
class A{
#OneToOne(cascade=CascadeType.ALL)
private B b;
}
class C{
#ManyToOne
private A a;
}
class B{
#OneToOne
private A a;
#MapKey(name = "name")
#OneToMany(cascade = CascadeType.ALL, ...)
#JoinColumn(...)
private Map<String C> cs;
}
How do I have to specify the mapping on B.cs to join where B.a == C.a?
Is this possible? Or do I have to change the property C.a to C.b? (I would prefer to keep it as it is, as the entity B is just a helper class.)
I also tried to change B to #Embeddable, but Map is not supported for embeddables.
JPA requires that all relationships be by Id (the foreign key references the primary key).
So, you need to either add a #ManyToOne from C to B.
Or, ensure that B's Id is the foreign key to A (add #Id on the #OneToOne from B to A and remove A's other #Id).
If B was a subclass of A instead of having a OneToOne this would also work.
If you are using EclipseLink, you can defined more complex criteria for a relationship. You would need to define the OneToMany's foreign keys using a DescriptorCustomizer and the OneToManyMapping API.
I think you can extends B from A.
If this doesn't work for you, maybe you can add a transient property to refer A's id,
#Transient
Integer getId1() {
return a.getId();
}
and join C using id1 instead of B's primary key.
Edit: This doesn't work.
This seems like it should be a pretty simple question, or at least have a simple answer. But - I'm really not a database guy, and I'm still pretty far down on the Hibernate learning curve. That said, here's the setup:
Consider a unidirectional many-to-many relationship between two entities, from Foo to Bar:
(pardon any typos below, the following is obviously a simplification of the actual code)
FooDTO.java:
#Entity
#Table(name = "MyDB.dbo.Foo")
class FooDTO implements Serializable
{
private int id;
private String name;
private Set<BarDTO> bars = new HashSet<BarDTO>();
...
#Fetch(FetchMode.JOIN)
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "MyDB.dbo.FooBar",
joinColumns = { #JoinColumn(name = "fooId") },
inverseJoinColumns = { #JoinColumn(name = "barId") })
public Set<BarDTO> getBars()
{
return bars;
}
public void setBars(Set<Bar> bars)
{
this.bars = bars;
}
}
BarDTO.java:
#Entity
#Table(name = "MyDB.dbo.Bar")
class BarDTO implements Serializable
{
private int id;
private String name;
...
}
On the TSQL side, the join table is set up like this:
CREATE TABLE [dbo].[FooBar](
[id] [int] NOT NULL IDENTITY PRIMARY KEY,
[fooId] [int] NOT NULL,
[barId] [int] NOT NULL,
CONSTRAINT fk_FooBar_FooId FOREIGN KEY (fooId) REFERENCES [dbo].[Foo](id),
CONSTRAINT fk_FooBar_BarId FOREIGN KEY (barId) REFERENCES [dbo].[Bar](id),
) ON [PRIMARY]
END
If I try to outright delete a BarDTO, I get a ConstraintViolationException because I haven't first deleted the row in the join table (duh).
Questions:
Can I get Hibernate to drop the rows in the join table automagically, when I delete a Bar? How?
If not, how do I select all the Foos that have a particular Bar, so I can remove that Bar from each relevant Foo's set of Bars?
With regard to the latter question, I think this could be done with either a NamedQuery or using the Criteria API, but I don't know specifically how to write such a query or which constraints to apply. I think it would a named query would look something like:
SELECT f FROM FooDTO f INNER JOIN ??? WHERE f.id = ???
but I'm not sure where the barId parameter would go, or how to join on the FooBar table since I don't declare it as an entity. (Side note, I also recall previous issues with trying to join in a named query - is joining in a named query impossible?)
Can I get Hibernate to drop the rows in the join table automagically, when I delete a Bar? How?
You need to remove the BarDTO instance from the collections in FooDTO that holds a reference on it.
If not, how do I select all the Foos that have a particular Bar, so I can remove that Bar from each relevant Foo's set of Bars?
The following should work:
SELECT f FROM FooDTO f WHERE :bar MEMBER OF f.bars
Or you could make the association bidirectional and simply call bar.getFoos().