How to do lazy loading of the non-owning entity in a unidirectional one-to-one mapping? - spring

#Entity
#Table(name = "...")
public class A
{
private Integer id;
...
...
private B b;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "b_id")
getB() {
return b;
}
setB(B b) {
this.b = b;
}
...other methods...
}
#Entity
#Table(name = "...")
public class B
{
private Integer id;
...
...
...methods...
}
When using
Query query = sessionFactory.getCurrentSession().createQuery()
with
query.uniqueResult()
or
query.list()
to fetch an instance of object A from the db,
I am getting the following error message:
org.hibernate.LazyInitializationException: could not initialize proxy [com.project.core.entity.B#261493] - no Session
The above query method is deprecated, however, I am working on an old project that uses Spring (not Spring Boot), and this is the only way I can go about it.
Now, I know that this may be because the hibernate session is already closed, however, I am using join fetch in my hibernate queries above.
I cannot use a shared primary key for the entities because in the future more entities like C, D, etc. will be added which will also have a unidirectional one-to-one mapping with B.
How do I go about fixing this problem? Thank you.

Related

How to Spring Cache JPA nested Objects?

Have Domain Object that has a series of Nested Objects thus. B and C are reference Data Objects. The values are static data that we never update in the table - ever.
Class A {
B b;
C c;
}
class B {
}
class C {
}
B, C etc are quite "heavy" and take some time to return from the DB.
I have a repository written thus:
class MyRepository extends CrudRepository<A, Long> {
}
I need a way to cache B, C so they are not queried everytime whenever I do this. Currently everytime I lookup A both B and C are retrieved from the DB even though they are static data:
MyRepository.findById(1L)
I KNOW I can use Spring Cache at the Service level. But what I'm interested in is DATABASE level Cache. B, C etc are using in many different places and more to come (including PUT/POST) so they all can make best use of database level caching of these reference data since using Spring Cache : Save operations dont get much benefits.
Please check if it is what you want.
maven add
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.2.Final</version>
</dependency>
application.properies add
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
entity modify
#Entity
#Cacheable
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Foo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private long id;
#Column(name = "NAME")
private String name;
// getters and setters
}
https://www.baeldung.com/hibernate-second-level-cache

Spring data jpa insert child into parent using rest call

Let's assume we have two entities A and B which have such a relation:
public class A {
#Id
private Integer id;
#OneToMany(mappedBy = "parent")
private Set<B> childs = new HashSet<>();
// getters and setters
}
public class B {
#Id
private Integer id;
#ManyToOne
private A parent;
// getters and setters
}
I'm having 2 repositories, one for each entity (ARepository and BRepository) and i'm using spring data rest starter so i've got generated rest api.
Let assume we have an instance of A, which has as id=12
In order to insert a B entity and link it to A i have to: POST the url http://localhost:8080/appContext/b/ and giving it a request body as follow: {"parent": "a/12"}
It works fine, but it is possible to have the same thing by posting http://localhost:8080/appContext/a/12/childs/ with the same request param except the "parent" JSON attribute ? as it's actually possible to fetch the childs of a parent using this last url.

Spring Data JPA Repository with Hibernate - persist (sql insert) parent entity but only update nested child entities

I'm developing a Spring Boot app with Spring data, JPA, Hibernate combination. Below is the scenario I'm struggling with where the expected behavior is to update only some child entities while the parent entity is being inserted as new.
Entity classes
#Entity
public class A {
#Id
private long id;
#ManyToOne
#JoinColumn (name = "B_ID")
#Cascade ( { CascadeType.ALL } )
private B b;
}
#Entity
#DynamicUpdate
public class B {
#Id
private long id;
#OneToMany (mappedBy = "b")
#Cascade ( { CascadeType.ALL } )
private Set<A> as;
#ManyToOne
#JoinColumn (name = "C_ID")
#Cascade ( { CascadeType.ALL } )
private C c;
}
#Entity
#DynamicUpdate
public class C {
#Id
private long id;
#OneToMany (mappedBy = "c")
#Cascade ( { CascadeType.ALL } )
private Set<B> bs;
#ManyToOne
#JoinColumn (name = "D_ID")
#Cascade ( { CascadeType.ALL } )
private D d;
#ManyToOne
#JoinColumn (name = "E_ID")
#Cascade ( { CascadeType.ALL } )
private E e;
}
#Entity
#DynamicUpdate
public class D {
#Id
private long id;
#OneToMany (mappedBy = "d")
#Cascade ( { CascadeType.ALL } )
private Set<C> cs;
}
#Entity
#DynamicUpdate
public class E {
#Id
private long id;
#OneToMany (mappedBy = "e")
#Cascade ( { CascadeType.ALL } )
private Set<C> cs;
}
Below are the steps I'm performing in the app:
Pull B from repo; if it exists, update some of its field values [Or] create new B with field values.
Create new instance of A and set B into A.
Persist A (by invoking JpaCrudRepository.save(A)).
Success part:
Everything works fine when B doesn't exist in repo already. Which means:
New instances of B (and C,D,E) are created,
This newly created B is set into A,
And A is persisted to repo successfully (a new row is inserted in all corresponding tables in DB/repo).
Failure part:
Now, when B already exists in repo, the existing B is pulled properly,
Some fields of B are updated while C, D, E are left untouched,
And this updated B is set into A.
But when trying to persist A now a Unique constraint violation is thrown on D and E.
All the entities are marked with following:
• ID as auto-generated column (used as PK implicitly),
• Mapping between entities using the ID column,
• CascadeType ALL wherever mappings like OneToMany, ManyToOne are applied,
• Dynamic update annotation.
So far what I could gather from around the web on this subject:
The JPA Repository doesn't have merge() or update() operations available explicitly, and the save() is supposed to cover those. And this save() operation works by calling merge() if the entity instance exists already Or by calling persist() if it is new. And digging further, the entity's ID column is being used to determine its existence in the repo. If this ID is null, entity is determined as new, and as existing if ID is not null.
So in my failure case above, as the existing B entity instance is being pulled from the repo, it already has a non-null ID.
So I'm not currently clear on what exactly I'm missing here.
I tried finding any matching solution online but couldn't arrive at one yet.
Can someone please help me identify what is wrong with my approach?
Found the issue and solution. As A and B were being created as new, subsequently C,D,E were all created as new which caused the violation. So to solve this I had to pull each existing entity of C,D,and E from the DB, and set them respectively/hierarchically inside the newly created B entity, before attempting to persist.
Basically, don't create new instance for an entity if it is nested within a parent entity which is about to be persisted.. and the goal is to update when a similar record already exists.

spring jpa projection nested bean

is it possible to have a projection with nested collection with Spring JPA?
I have the following 2 simple entity (to explain the problem)
#Entity
#Table(name = "person")
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList = new ArrayList<>();
}
#Entity
#Table(name = "address")
public class Address implements Serializable {
private Integer id;
private String city;
private String street;
}
Is it possible to have a projection of Person with following attributes filled in ? {person.name, address.city}
I might be wrong in semantics of word Projection. but the problem is what i need to achieve. Maybe it is not possible with Projection, but is there another way to achieve the end goal? Named Entity graph perhaps ?
P.S. please suggest a solution for Spring JPA not Spring Jpa REST
thanks in advance
You're right, Entity Graphs serve this exact purpose - control field loading.
Create entity graphs dynamically from the code or annotate target entities with Named Entity Graphs and then just use their name.
Here is how to modify your Person class to use Named Entity Graphs:
#Entity
#Table(name = "person")
#NamedEntityGraph(name = "persion.name.with.city",
attributeNodes = #NamedAttributeNode(value = "addressList", subgraph = "addresses.city"),
subgraphs = #NamedSubgraph(name = "addresses.city", attributeNodes = #NamedAttributeNode("city")))
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList;
}
And then when loading your person:
EntityGraph graph = em.getEntityGraph("person.name.with.city");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
return em.find(Person.class, personId, hints);
The same applies for queries, not only em.find method.
Look this tutorial for more details.
I think that that's not usual scenario of Data JPA usage. But you can achieve your goal with pure JPQL:
SELECT a.street, a.person.name FROM Address a WHERE …
This solution has 2 drawbacks:
It forces you to have bidirectional relationship Address ←→ Person
It returns List
Another solution (and that's preferred JPA way) is to create DTO like this:
class MyPersonDTO {
private String personName;
private List<String> cities;
public MyPersonDTO(String personName, List<Address> adresses) {
this.personName = personName;
cities = adresses
.stream()
.map(Address::getCity)
.collect(Collectors.toList());
}
}
And the execute JPQL query like this:
SELECT NEW package.MyPersonDTO(p.name, p.addressList) FROM Person p WHERE …
Return type will be List<MyPersonDTO> in that case.
Of course you can use any of this solutions inside #Query annotation and it should work.

Spring JPA OneToOne out of a OneToMany with only the fist entry

my problem can be broken down to this little example:
I have a entity class A and a entity class B. A has a List of B objects. Now there is always only one B relevant. So I do not want to load all B's of an A, only to access this one B (last inserted B inside a A).
The question: Can I manipulate an entity without an service, so that there is a #Transient variable, that is always the newest B? And also without saving the newest B separately in A. Is there a way to achieve this?
class B{
#Id
#GeneratedValue
private Long id;
#Column(nullable=false)
private String name;
#Column(nullable=false)
private Date created = new Date();
}
class A{
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("created ASC")
private List<B> b;
#Transient
private B newestB; // Here should be only the newest B
}
Yes. Forget storing the newest B as a variable and instead simply add a getter for it:
#Transient
public B getNewestB() {
return b.get(b.size() -1);
}
This will solve your problem under the assumption that b is set to FetchType.EAGER. Fetching using b's getter and FetchType.LAZY may not be so straight forward as Spring may rely on an AOP proxy call to trigger the lazy load (you'd need to experiment).
However, I'd discourage both these approaches. You're effectively trying to fit business logic into your Entity. Why not keep your entity clean and perform this query using B's repository?
E.g.
public interface BRepository extends CrudRepository<B, Long> {
#Query(...) //query to get newest B for specified A
B getNewest(A a)
}

Resources