I have the following model:
#Table(name = "foo")
public class Foo {
#ManyToOne(fetch = FetchType.LAZY)
private Bar bar;
}
Using Entity Framework in .NET in a similar situation, I could eagerly bring the Bar property with something like:
context.Foo.Include(f => f.bar).First()
Is there anything equivalent with Hibernate?
My situation is that I'm saving an object with a lazy property into a session in my server. Then when I retrieve the session properties, I cannot access the lazy property for the Hibernate Session is already gone. I cannot put this property as EAGER because it is inherited from a #MappedSuperclass used by a lot of other classes.
Thanks for any help.
JPA EntityGraph:
#Entity
#Table(name = "foo")
#NamedEntityGraph(name = "foo.bar",
attributeNodes = #NamedAttributeNode("bar")
)
public class Foo {
#ManyToOne(fetch = FetchType.LAZY)
private Bar bar;
}
Foo foo = entityManager.find(
Foo.class,
id,
Collections.singletonMap(
"javax.persistence.fetchgraph",
entityManager.getEntityGraph("foo.bar")
)
);
You can see another example and more detail explanation there.
Hibernate profiles:
#Entity
#Table(name = "foo")
#FetchProfile(
name = "foo.bar",
fetchOverrides = {
#FetchProfile.FetchOverride(
entity = Foo.class,
association = "bar",
mode = FetchMode.JOIN
)
}
)
public class Foo {
#ManyToOne(fetch = FetchType.LAZY)
private Bar bar;
}
session.enableFetchProfile("foo.bar");
Foo foo = session.byId(Foo.class).load(id);
You can use a JPQL query with FETCH JOIN:
List<Foo> l = em.createQuery(
"SELECT f FROM Foo f JOIN FETCH f.bar", Foo.class)
.getResultList();
In this way all Foo class instances will be loaded with all Bar instances already fetched. You can tailor the query to fit your needs.
Related
I have got many OneToMany relationships that needs to be loaded eagerly in most cases like this:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "bar")
#Fetch(FetchMode.SELECT)
#BatchSize(size = 10)
However, I need, in a very few cases, to change it in hibernate criteria to lazy load (still select and batch). But I do not know how to do it.
When I try solution from here -
criteria.setFetchMode("propertyName", FetchMode.SELECT);
it still loads eagerly.
My criteria looks something like this now:
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Bar.class);
criteria.add(Restrictions.eq("valid", true));
criteria.add(Restrictions.idEq(barId));
Bar result = (Bar) criteria.uniqueResult();
Here is entity, just for illustration purposes...
#Entity
#Table(name = "BAR", schema = "UD")
public class Bar {
private List<Foo> foos;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "bar")
#Fetch(FetchMode.SELECT)
#BatchSize(size = 10)
public List<Foo> getFoos() {
return foos;
}
}
I am using Hibernate 5.1.10.Final
I don't know is it possible to do on-demand fetching but there is one technique available for this, you can use fetch Bar object using constructor inside custom query:
#Query("SELECT new BAR( <add other required fields except FOOs> ) from BAR")
Note: This is different from LAZY loading. If you need Foo later than you have to make DB call again.
I want to select all Foo entities that have any Bars that have status new.
Here's what I tried:
#Entity
#Table(name = "FOO")
class Foo {
#Id
#Column(name = "ID")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#OneToMany
private Set<Bar> bars;
//...
}
public interface FooRepository extends CrudRepository<Foo, Long> {
#Query("select m from Foo f where f.bars.status = 'NEW' ")
public Page<Foo> findByBarStatus(Pageable pageable);
}
But I get :
org.hibernate.exception.SQLGrammarException: could not prepare statement
I also tried writing join statement instead:
select f from Foo f inner join f.bars b where b.status = 'NEW'
SELECT f FROM Foo AS f JOIN f.bars AS b WHERE b.status = 'NEW'
I think you're missing something like this
#OneToMany(mappedBy = "foo")
private Set<Bar> bars;
joining for one-to-many relationship can sometimes be tricky. (There is another answer doing it right, that you can refer to)
In this case, you may do it in reverse (assuming you have the relationship being bi-directional), by looking up from the Bar side:
select bar.foo from Bar bar where bar.status = 'NEW'
I have two entities that have a relation OneToOne, like this:
#Entity
#Table(name = "FOO")
Foo{
#OneToOne(fetch = FetchType.LAZY, mappedBy = "foo")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
#JoinColumn(name = "BAR_ID")
private Bar bar;
// getters and setters
}
#Entity
#Configurable
#Table(name = "BAR")
Bar{
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FOO_ID")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private Foo foo;
// getters and setters
}
In the Service layer, I make the connection by setting Bar in Foo:
Bar.setFoo(foo);
barDAO.saveOrUpdate(bar);
Wich saves the foo id in the Bar table. But the opposite doesn't happen. Is it possible for hibernate to save both ids making only one set? I thought this would be working already
You need to understand the relationships well first. As much I see here you might trying to have a bidirectional OneToOne relationship between Foo and Bar.
#Entity
#Table(name = "FOO")
Foo {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "BAR_ID")
private Bar bar;
// getters and setters
}
#Entity
#Table(name = "BAR")
Bar{
#OneToOne(mappedBy = "bar")
private Foo foo;
// getters and setters
}
In the bidirectional association two sides of association exits –
owning and inverse. For one to one bidirectional relationships, the
owning side corresponds to the side that contains the appropriate
foreign key.
Here the owning side is the Foo and BAR_ID would be that foreign key. Having join column in both end doesn't make sense. And Relation will be cascaded from the Foo to Bar. And the inverse side is Bar here that needs to be annotated with mapped by value of the owning side reference.
Now if you set Bar object in Foo it will persist the Bar object along with the mapping with Foo. Doing the reverse thing doesn't make sense. isn't it ?
You are missing the opposite side of the relation.
If you say bar.setFoo(foo) then after you have to say foo.setBar(bar) or of course you can do this within the setFoo method as well.
Cascading means that it will trigger the operation on the relation, however in your case, the relation was unfinished as one side was missing.
I am working on a project using Hibernate 4.3.4 to access a Postgres DB. We have two entities which are linked via a ManyToMany Association.
The code and the associations currently work, in that adding an EntityB to EntityA's collection will automatically add the EntityA to the EntityB's collection once the Session is committed. However, the issue I'm having is that when I try to work on the EntityB's EntityAs, which should include the EntityA I just created, EntityA is not in that collection (It is empty). Example code is here:
#Entity
#Table(name = "entity_a")
public class EntityA {
private Set<EntityB> entityBs = new HashSet<EntityB>(0);
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "entitya_entityb",
joinColumns = { #JoinColumn(name = "entitya_id") },
inverseJoinColumns = { #JoinColumn(name = "entityb_id") })
public Set<EntityB> getEntityBs()
{
return entityBs;
}
public void setEntityBs(Set<EntityB> entityBs)
{
this.entityBs = entityBs;
}
}
#Entity
#Table(name = "entity_b")
public class EntityB {
private Set<EntityA> entityAs = new HashSet<EntityA>(0);
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "entitya_entityb",
joinColumns = { #JoinColumn(name = "entityb_id") },
inverseJoinColumns = { #JoinColumn(name = "entitya_id") })
public Set<EntityA> getEntityAs()
{
return entityAs;
}
public void setEntityAs(Set<EntityA> entityAs)
{
this.entityAs = entityAs;
}
}
/**
* HTTP REST Resource to create Entities and persist them. We do some basic logic when we create them to show the problem.
*/
#Path("/battleRhythm")
#Singleton
public class HttpResource
{
#POST
#Consumes("application/json")
public void createEntityA() {
Session hibernateSession = SessionFactory.getCurrentSession(); // SessionFactory specifics not included
hibernateSession.getTransaction().begin();
// Add an EntityB to the new EntityA
EntityA entityA0 = new EntityA();
EntityB entityB0 = new EntityB0();
entityA.getEntityBs().add(entityB0);
// Persist the new EntityA
EntityADao.getInstance().save(entityA0);
// Try to get this EntityA from EntityB
Set<EntityA> associatedEntityAs = entityB0.getEntityAs(); // Doesn't contain any EntityAs!
hibernateSession.getTransaction().commit();
}
}
Here's the question:
Can I make Hibernate automatically add the EntityA0 to EntityB0's collection when I save EntityA0, without committing the transaction? Is this possible? How?
Caveat : The example above does not fully reflect this, but we perform similar operations on both Entities, so having an "owner" in the traditional Hibernate sense (using the mappedBy = "" Attribute configuration) is not an ideal option. I don't want to try to convince everyone to only ever use EntityB.getEntityAs().add(EntityB0) in CreateEntityA(). It's too confusing.
You don't have the choice. There MUST be an owner side, and there MUST be an inverse side. And it's YOUR responsibility to maintain both sides of the association: don't expect to have B inside A's collection of Bs when you only add A to B (and vice-versa)
Now, nothing forbids you to have a methods addB(B b) inside A that adds b to A's collection of Bs, and which adds this to B's collection of As. And you can of course also have a method addA(A a) in B that does the same thing.
I have the following entities:
#Entity
public class Foo {
#ManyToOne(optional = false) // I've tried #OneToOne also, same result
#JoinColumn(name = "bar_id")
private Bar bar;
// this is a business key, though not mapped as unique for legacy reasons
#Column(nullable = false)
private long fooNo;
// getters/setters + other properties
}
#Entity
public class Bar {
#OneToOne(optional = true, mappedBy = "bar", cascade = CascadeType.ALL)
private Foo foo;
// getters/setters + other properties
}
NOTE: This is the correct mapping: #ManyToOne and #OneToOne (it wasn't designed by me). I have tried #OneToOne on both sides also, with the same result.
Basically, I can have a Bar without a Foo object, but everytime I have a Foo, there has to be a Bar associated with it. Foo is considered the parent object (which is why its the owner of the association), but Bar can stand alone in certain cases.
I then load a Foo object like this:
SELECT f FROM Foo f WHERE f.fooNo = :fooNo
foo.getBar() correctly fetches the appropriate Bar, as expected. However, foo.getBar().getFoo() is null. It seems the other side of this relationship is not correctly initialized by JPA/hibernate. Any ideas why this is happening and how I can fix it?
I use Hibernate 3.2.1 as my JPA implementation, which we are using through EJB3 beans (though that is probably irrelevant).
Are you sure about your assotiation? ManyToOne usually implies oneToMany on other.