Update/Save data in Spring JPA - spring

I hope someone can help me solve this problem
I am using spring and JPA to save data.
When I try to save calling my DAO, not all the data gets saved.
Consider the following structure
class User
var name
var surname
#OneToMany(mappedBy = "rule",cascade = CascadeType.PERSIST)
#Fetch(FetchMode.SUBSELECT)
set<Address> address;
#OneToMany(mappedBy = "rule",cascade = CascadeType.PERSIST)
#Fetch(FetchMode.SUBSELECT)
set<Job>job;
class Address
List<AddressList>addressList;
class Job
List<JobList>jobList;
What basically happens is that name and surname changes but if I make any change related to the address or job class, these aren't committed.
However, if I delete the user, it works,if I retrieve the user information(addresses,jobs),these are retrieved correctly.
Any advice on what could be the issue?

You're only cascading the persist operation. When you update an already persisted entity, a merge operation will be performed. So, I suggest to cascade merge operation as well: CascadeType.MERGE if you want to save the related entities on update, too.

Try cascade type all --- CascadeType.ALL.
For orphan removals you should use :
CascadeType.DELETE_ORPHAN - if you are using hibernate
orphanRemoval = true - if you are using jpa 2.0
manual removal - if none of the above apply

Have decalre your transient object #Transient ? ok BTw --
follw below steps:
declare your teansient objects as #Transient
Use cascade type all --- CascadeType.ALL.
try to flush your entitymanager by using entityManager.flush() then perform persist() or merge() operation.

Related

Transaction getting rolled back on persisting the entity from Many to one side

I have this association in the DB -
I want the data to be persisted in the tables like this -
The corresponding JPA entities have been modeled this way (omitted getters/setters for simplicity) -
STUDENT Entity -
#Entity
#Table(name = "student")
public class Student {
#Id
#SequenceGenerator(name = "student_pk_generator", sequenceName =
"student_pk_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"student_pk_generator")
#Column(name = "student_id", nullable = false)
private Long studentId;
#Column(name = "name", nullable = false)
private String studentName;
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
private Set<StudentSubscription> studentSubscription;
}
STUDENT_SUBSCRIPTION Entity -
#Entity
#Table(name = "student_subscription")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class StudentSubscription {
#Id
private Long studentId;
#ManyToOne(optional = false)
#JoinColumn(name = "student_id", referencedColumnName = "student_id")
#MapsId
private Student student;
#Column(name = "valid_from")
private Date validFrom;
#Column(name = "valid_to")
private Date validTo;
}
LIBRARY_SUBSCRIPTION Entity -
#Entity
#Table(name = "library_subscription",
uniqueConstraints = {#UniqueConstraint(columnNames = {"library_code"})})
#PrimaryKeyJoinColumn(name = "student_id")
public class LibrarySubscription extends StudentSubscription {
#Column(name = "library_code", nullable = false)
private String libraryCode;
#PrePersist
private void generateLibraryCode() {
this.libraryCode = // some logic to generate unique libraryCode
}
}
COURSE_SUBSCRIPTION Entity -
#Entity
#Table(name = "course_subscription",
uniqueConstraints = {#UniqueConstraint(columnNames = {"course_code"})})
#PrimaryKeyJoinColumn(name = "student_id")
public class CourseSubscription extends StudentSubscription {
#Column(name = "course_code", nullable = false)
private String courseCode;
#PrePersist
private void generateCourseCode() {
this.courseCode = // some logic to generate unique courseCode
}
}
Now, there is a Student entity already persisted with the id let's say - 100.
Now I want to persist this student's library subscription. For this I have created a simple test using Spring DATA JPA repositories -
#Test
public void testLibrarySubscriptionPersist() {
Student student = studentRepository.findById(100L).get();
StudentSubscription librarySubscription = new LibrarySubscription();
librarySubscription.setValidFrom(//some date);
librarySubscription.setValidTo(//some date);
librarySubscription.setStudent(student);
studentSubscriptionRepository.save(librarySubscription);
}
On running this test I am getting the exception -
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.springboot.data.jpa.entity.Student; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.springboot.data.jpa.entity.Student
To fix this I attach a #Transactional to the test. This fixed the above exception for detached entity, but the entity StudentSubscription and LibrarySubscription are not getting persisted to the DB. In fact the transaction is getting rolled back.
Getting this exception in the logs -
INFO 3515 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext#35390ee3 testClass = SpringDataJpaApplicationTests, testInstance = com.springboot.data.jpa.SpringDataJpaApplicationTests#48a12036, testMethod = testLibrarySubscriptionPersist#SpringDataJpaApplicationTests, testException = [null], mergedContextConfiguration = [MergedContextConfiguration#5e01a982 testClass = SpringDataJpaApplicationTests, locations = '{}', classes = '{class com.springboot.data.jpa.SpringDataJpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer#18ece7f4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer#264f218, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer#0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer#2462cb01, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer#928763c, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer#0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer#7c3fdb62, org.springframework.boot.test.context.SpringBootTestArgs#1, org.springframework.boot.test.context.SpringBootTestWebEnvironment#1ad282e0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
Now I have couple of questions -
Why am I getting detached entity exception. When we fetch an entity from the DB, Spring Data JPA must be using entityManager to fetch the entity. The fetched entity gets automatically attached to the persistence context right ?
On attaching #Transactional on the test, why the transaction is getting rolledback, and no entity is getting persisted. I was expecting the two entities - StudentSubscription and LibrarySubscription should've been persisted using the joined table inheritance approach.
I tried many things but no luck. Seeking help from, JPA and Spring DATA experts :-)
Thanks in advance.
Let me add a few details that outline a couple of design problems with your code that significantly complicate the picture. In general, when working with Spring Data, you cannot simply look at your tables, create cookie-cutter entities and repositories for those and expect things to simply work. You need to at least spend a bit of time to understand the Domain-Driven Design building blocks entity, aggregate and repository.
Repositories manage aggregates
In your case, Student treats StudentSubscriptions like an entity (full object reference, cascading persistence operations) but at the same time a repository to persist the …Subscriptions exists. This fundamentally breaks the responsibility of keeping consistency of the Student aggregate, as you can simply remove a …Subscription from the store via the repository without the aggregate having a chance to intervene. Assuming the …Subscriptions are aggregates themselves, and you'd like to keep the dependency in that direction, those must only be referred to via identifiers, not via full object representations.
The arrangement also adds cognitive load, as there are now two ways to add a subscription:
Create a …Subscription instance, assign the Student, persist the subscription via the repository.
Load a Student, create a …Subscription, add that to the student, persist the Student via it's repository.
While that's already a smell, the bidirectional relationship between the …Subscription and Student imposes the need to manually manage those in code. Also, the relationships establish a dependency cycle between the concepts, which makes the entire arrangement hard to change. You already see that you have accumulated a lot of (mapping) complexity for a rather simple example.
What would better alternatives look like?
Option 1 (less likely): Students and …Subscriptions are "one"
If you'd like to keep the concepts close together and there's no need to query the subscriptions on their own, you could just avoid those being aggregates and remove the repository for them. That would allow you to remove the back-reference from …Subscription to Student and leave you with only one way of adding subscriptions: load the Student, add a …Subscription instance, save the Student, done. This also gives the Student aggregate its core responsibility back: enforcing invariants on its state (the set of …Subscription having to follow some rules, e.g. at least one selected etc.)
Option 2 (more likely): Students and …Subscriptions are separate aggregates (potentially from separate logical modules)
In this case, I'd remove the …Subscriptions from the Student entirely. If you need to find a Students …Subscriptions, you can add a query to the …SubscriptionRepository (e.g. List<…Subscription> findByStudentId(…)). As a side effect of this you remove the cycle and Student does not (have to) know anything about …Subscriptions anymore, which simplifies the mapping. No wrestling with eager/lazy loading etc. In case any cross-aggregate rules apply, those would be applied in an application service fronting the SubscriptionRepository.
Heuristics summarized
Clear distinction between what's an aggregate and what not (the former get a corresponding repository, the later don't)
Only refer to aggregates via their identifiers.
Avoid bidirectional relationships. Usually, one side of the relationship can be replaced with a query method on a repository.
Try to model dependencies from higher-level concepts to lower level ones (Students with Subscriptionss probably make sense, a …Subscription without a Student most likely doesn't. Thus, the latter is the better relationship to model and solely work with.)
The transaction is getting rolled back because the test is doing DB updates in the test method.
#Transactional does auto rollback if the transaction includes any update DB. Also here is the compulsion to use transaction because EntityManager gets closed as soon as the Student entity gets retrieved, so to keep that open the test has to be within the transactional context.
Probably if I had used a testDB for my testcases then probably spring wouldn't haveve been rolling back this update.
Will setup an H2 testDb and perform the same operation there and will post the outcome.
Thanks for the quick help guys. :-)
Why am I getting detached entity exception. When we fetch an entity from the DB, Spring Data JPA must be using entityManager to fetch the entity. The fetched entity gets automatically attached to the persistent context right ?
Right, but only for as long as the entityManager stays open. Without the transactional, as soon as you return from studentRepository.findById(100L).get();, the entityManager gets closed and the object becomes detached.
When you call the save, a new entityManager gets created that doesn't contain a reference to the previous object. And so you have the error.
The #Trannsaction makes the entity manager stay open for the duration of the method.
At least, that's what I think it's happening.
On attaching #Transactional on the test, why the transaction is getting rolledback,
With bi-directional associations, you need to make sure that the association is updated on both sides. The code should look like:
#Test
#Transactional
public void testLibrarySubscriptionPersist() {
Student student = studentRepository.findById(100L).get();
StudentSubscription librarySubscription = new LibrarySubscription();
librarySubscription.setValidFrom(//some date);
librarySubscription.setValidTo(//some date);
// Update both sides:
librarySubscription.setStudent(student);
student.getStudentSubscription().add(librarySubscription);
// Because of the cascade, saving student should also save librarySubscription.
// Maybe it's not necessary because student is managed
// and the db will be updated anyway at the end
// of the transaction.
studentSubscriptionRepository.save(student);
}
In this case, you could also use EntityManager#getReference:
#Test
#Transactional
public void testLibrarySubscriptionPersist() {
EntityManager em = ...
StudentSubscription librarySubscription = new LibrarySubscription();
librarySubscription.setValidFrom(//some date);
librarySubscription.setValidTo(//some date);
// Doesn't actually load the student
Student student = em.getReference(Student.class, 100L);
librarySubscription.setStudent(student);
studentSubscriptionRepository.save(librarySubscription);
}
I think any of these solutions should fix the issue. Hard to say without the whole stacktrace.

How can I get an Entity with its referenced entity ids in a ManyToMany relation?

I have an basic spring application that uses hibernate and mapstruct
There are two Entities, each are implemented to have their subchild entities as List attribute in a ManyToMany relation
So there is
EntityA.class
with List<EntityB> (fetchType Lazy)
and vice versa
Now when my client calls, it wants to get a DTO that represents like following:
EntityADTO
with List<Long> entityBIds
How can I get my EntityA with only the Ids of EntityB most efficient and without loading the complete EntityB and post process it after?
Thanks a lot!
The #ManyToMany association information is persisted in a dedicated (join-)table and is loaded lazily on collection access, so there needs to be another query.
Instead of querying for the complete information of all associated entities, you could specifically query only for the needed id property.
Possible queries could look e.g. like this:
// Spring-Data repository (requires an extra interface for the result):
interface IdOnly(){
Long getId();
}
interface EntityBRepository extends JpaRepository<EntityB, Long> {
List<IdOnly> getIdByEntityAId(Long enitityAId);
}
// alternative JPQL query (does not need the interface):
#Query("SELECT b.id FROM EntityB b JOIN b.entityAs as a WHERE a.id=:entityAId")
List<Long> getIdByEntityAIdJpaQuery(#Param("enitityAId") Long enitityAId);
This way, only the needed EntityB ids for an associated EntityA are loaded from the DB.
For even further tuning, one could also write a native query directly accessing only the join-table, which avoids all joins:
#Query(nativeQuery = true, //
value = "SELECT entityBId FROM entityA_entityB WHERE enitityAId=:enitityAId")
List<Long> getIdByEntityAIdNative(#Param("enitityAId") Long enitityAId);
For executing the query when mapping with mapstruct, you can use the spring repository bean e.g. as described here: https://stackoverflow.com/a/51292920
In addition to #Fladdimir's answer which is a great approach if you only need the list of values occasionally, JPA allows defining Entity Graphs that can specify what in an object graph you want loaded. This can allow you to define your entity and specific attributes from child/referenced entities in the graph, allowing objects to be returned but the bulk of the data unfetched. This can allow you to process Entity B instances, but without them being fully populated.
There are many tutorials but I've referenced https://www.baeldung.com/jpa-entity-graph more than once. As the tutorial referenced mentions though, Hibernate might have some issues with how it handles attributes that are normally eagerly fetched, so it might not work the way you want (but will with other JPA providers like EclipseLink, which is where I've used this).
Alternatively, if this is a collection of IDs you are going to want/need frequently, you can modify your object model to have them fetched differently.
public class EntityA {
..
#ElementCollection
#CollectionTable(name = "RELATION_TABLE_NAME", joinColumns = #JoinColumn(name = "A_ID", insertable=false, updatable=false))
#Column(name = "B_ID", insertable=false, updatable=false)
List<Long> bIds;
}
This allows fetching the foriegn keys automatically in your AEntity. I've made it read-only, assuming you'd keep the existing A->B relationship and use that to set things. Doing so though means that these two relationships are entirely separate, and so might result in different queries to fetch this same set of data.
If that is a concern, you can alter things again, and remove the existing A->B relationship, and stick it in an intermediary object AB.
public class EntityA {
..
#ElementCollection
#CollectionTable(name = "RELATION_TABLE_NAME", joinColumns = #JoinColumn(name = "A_ID"))
List<AB> listOfBs;
}
#Embeddable
public class AB {
#Column("B_ID", insertable=false, updatable=false)
Long bId;
#ManyToOne(fetch=LAZY)
#JoinColumn(name = "B_ID")
B b;
}
This would allow you to fetch As and use B's ID values without having to fetch from the B table. Note that I've marked the basic bId property as read-only, assuming that your existing app would be setting things by assigning a B reference to the relationship, but you could mark the relationship as read-only instead, and set the FK value using the bId. This might be more efficient long term, as you don't have to look up the B instance to set the relationship.
Alternatively again, you can make AB an entity instead of an embeddable, and allow it to exist and be queried upon outside of As and Bs. There are quite a few options though, and ways to map it, and not likely necessary for a simple model and use case.

JPA - Auto-generated field null after save

I have an Account entity and I'm trying to persist it using save function. My code:
#Override
public Account createAccount(String pin) {
Account account = new Account();
account.setBalance(0L);
account.setPin(pin);
return accountRepository.save(account);
}
Now my entity class has an autogenerated field called accountNumber. My entity class:
#Entity
#Table(name = "accounts")
#Data
public class Account {
#Column(name = "account_number", length = 32, insertable = false)
private String accountNumber;
private Long balance;
}
Now after calling save, the entity returned has accountNumber as null but i can see in the intellij database view that it is actually not null. All the other auto-generated fields like id etc are there in the returned entity just the accountNumber is null. Default value for accountNumber is set in the sql file :
ALTER TABLE accounts
ALTER COLUMN account_number SET DEFAULT DefaultValueSerializer(TRUE, TRUE, 12);
Here, DefaultValueSerializer is the function which is generating the account number.
I've tried other solutions available here like using saveAndFlush() etc, nothing worked in my case. What can be an issue?
As mentioned in comment Hibernate is not aware about what happens in database engine level so it does not see the value generated.
It would be wise to move generation of account number to JPA level instead of using db defaults.
I suggest you to study annotations #GeneratedValue and related stuff like #SequenceGenerator. That way the control of generating account number is in JPA level and there is no need for stuff like refreshing entity after save.
One starting point: Java - JPA - Generators - #SequenceGenerator
For non-id fields it is possible to generate value in method annotated with #PrePersist as other answer suggests but you could do the initialization already in the Accounts constructor .
Also see this answer for options.
You can create an annotated #PrePersist method inside the entity in which you set its fields to their default value.
That way jpa is going to be aware of the default.
There are other such annotation avaiable for different entity lifecycle event https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.html
P.s. if you decide to go this way remember to remove the insertable = false
Use
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
for your IDs. And also leave your saving to saveAndFlush so you can immediately see the changes, If any. I'd also recommend separating IDs and account numbers. They should not be the same. Try debugging your program and see where the value stops passing around.

#Transactional(readOnly = true) leads to LazyInitializationException

I have a many-to-many relation with an additional column in the link table. I've configured it in a way that the owning side fetches children eager (so I don't get LazyInitializationException) and in the opposite direction it is lazy. This works.
I now wanted to fine-tune the transactions (before there was just #Transactional on class level of DAO and Service classes. I set method getById to readOnly = true:
#Transactional(readOnly = true)
public Compound getById(Long id) {
return compoundDAO.getById(id);
}
After this change I get a LazyInitializationException in following snippet:
Compound compound = compoundService.getById(6L);
Structure structure = compound.getComposition().get(0).getStructure();
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException
If I remove (readOnly = true) this works! Can anyone explain this behavior? I use Spring + Hibernate. Kind of confusing as I don't see any reason why this should affect which data is loaded?
EDIT:
Snippets of relationship definitions. This is a many-to-many with a column in the link table.
Owning side (eg Compound contains Structures):
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound",
cascade = CascadeType.ALL, orphanRemoval = true)
#OrderBy("pk.structure.id ASC")
private List<CompoundComposition> composition = new ArrayList<>();
Belongs to side:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure",
cascade = CascadeType.ALL)
#OrderBy("pk.compound.id ASC")
private List<CompoundComposition> occurence;
Many-To-One in #Embeddable ID class
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Compound getCompound() {
return compound;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
EDIT 2:
Stack Trace
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na]
at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na]
EDIT 3:
Also see my comment:
Log is very different with readOnly and it is missing the part were the relations are loaded, eg. some selects are missing in the log.
EDIT 4:
So I tired with a basic DriverManagerDataSource and no Connection pool. The issue is exactly the same. For me looks like an issue in Hibernate.
This is just wow. I'm starting to understand why some people hate ORMs...Just feels like I'm constantly having to spend hours to solve a weird issue and the solution is a very specific set of annotations + some code to work around the limitations of said annotations.
First to why this happens (why meaning with which annotations, but not in terms of making logical sense, which is the actual problem here as using common-sense is useless. Only trial and error helps). In the owning side, in #OneToMany I have orphanRemoval = true (which I have found out is required for consistency. one would think database constraints should handle that...just one of the many things that can drive you crazy.). It seems that if the transaction is not read-only, then this setting leads to some data being fetched even so its lazy, namely here:
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
In a read-only transaction, this fetching does not happen. I would guess because if you can't change anything you will also not have to remove orphans and hence any data that the logic behind this setting requires is not needed in a read-only tx.
So the obvious solution would be in above relation to change to FetchType.EAGER. Wrong! If you do that you will not be able to update the owning side (Compound) using session.merge. This will lead to a StackOverFlowError.
The real solution was actually already mentioned. Just leave the config as is but explicitly load the desired relations in the Service layer:
#Transactional(readOnly = true)
#Override
public Compound getById(Long id) {
Compound compound = compoundDAO.getById(id);
for (CompoundComposition composition : compound.getComposition()){
Hibernate.initialize(composition.getStructure());
}
return compound;
}
I admit I'm tending to fall in the premature optimization trap. This doesn't look very efficient and also seems to break how SQL works in the first place. But then I'm in the lucky position that in most cases CompoundComposition will contain only 1 or 2 elements.
Perhaps you could put
value.getComposition().get(i).getStructure();
in the body of the getById() method, so that the lazy loading happens within the transaction. I realize in this case you'd have to loop over i which might be inconvenient.
Two things :-
Lazy fetch works on Collections Interface. Since ...
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
... this is not a collection interface (like List<Structure> would have been), it will be fetched in Eager fetch mode.
Make service method as transactional. It seems that after fetching from the dao layer, your structure is detached with NEVER flush mode. This is the underlying ORM issue I guess.

When are Entities-Classes enhanced and what for? jpa, spring, hibernate, javassist

I am using spring 3.0.6, jpa 2.0, hibernate 3.6.8.
My question is, in which situations is javassist used to create "proxy" for a EntityClass? And what is reason of this proxy?
I have the following Entity:
#Entity
public MyEntity{
..
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "adresseID")
private Adresse adresse;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "myEntity")
private List<Parameter> parameters;
..
}
When I load a MyEntity from db, the class of entity is something like MyEntity__$$_javassist. Why is it done? What for? I think that just regular class MyEntity can be used here .
To implement lazy loading, we can:
for #OneToMany - PersistenceBag can be used here
for #ManyToOne - here should be used "enchancedClass" like Adress_$$_javassist
So what is reason for enchancing MyEntity? Where I can read something more about it? Which book/article/blog can you recommend me?
The primary reason why entity classes are enhanced is that JPA (or Hibernate) need to track entity objects state.
In particular JPA must be aware if given entity field is "dirty" - it was modified by user, but this change is not yet reflected in database, so JPA must synchronize it with database when transaction is commited.
The other case is "loaded" state of the entity field. Any field can be assigned to be lazy loaded. When such field is about to be used, JPA must be aware that database query has to be performed to initialize value of that field.
Hibernate's default is to use runtime enhacement - the proxy is just a subclass of the entity with extra stuff added.
Some general ideas are outlined here.

Resources