My requirement is - based on the value of a flag(say for eg. skipDbUpdate) in properties file, I want to save/update the entities or skip these entities in the ongoing transaction.
I have implemented a listener on the entity which will throw an exception if this flag is true, but now I have to enhance this behaviour to not throw an error but skip the update of the entity. I tried the below options :
#Immmutable annotation on the entity - Im not able to make this flag based, application uses using spring but I'm not able to combine #ConditionalOnProperty annotation with #Entity and #Immutatble annotations.
#Entity #Immutatble public class EntityA {}
using updateable=false, insertable=false on each field of the entity inside #Column annotation - again, Im not able to make this flag based (same reason as above).
#Column(insertable = false, updatable = false)
Calling entityManager.detach(o) method inside the Listener when flag is true, as suggested in this question - How to make an Entity read-only?
But this is trying to save the entities from other transaction and throwing the error - "
java.lang.RuntimeException: org.hibernate.HibernateException: Found two representations of same collection:"
As this is old code base which uses spring only, I cannot use annotations that easily.
Please suggest which is the best possible way to fulfill this requirement?
Thanks
Lets assume you are using CRUDRepository you can extend the interface and override the save method & in this method you can check foe the flag if skipDb then do nothing else call super.save(….)
Related
I'm updating an existing code that handles the copy or raw data from one table into multiple objects within the same database.
Previously, every kind of object had a generated PK using a sequence for each table.
Something like that :
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
In order to reuse existing IDs from the import table, we removed GeneratedValue for some entities, like that :
#Id
#Column(name = "id")
private Integer id;
For this entity, I did not change my JpaRepository, looking like this :
public interface EntityRepository extends JpaRepository<Entity, Integer> {
<S extends Entity> S save(S entity);
}
Now I'm struggling to understand the following behaviour, within a spring transaction (#Transactional) with the default propagation and isolation level :
With the #GeneratedValue on the entity, when I call entityRepository.save(entity) I can see with Hibernate show sql activated that an insert request is fired (however seems to be only in the cache since the database does not change)
Without the #GeneratedValue on the entity, only a select request is fired (no insert attempt)
This is a big issue when my Entity (without generated value) is mapped to MyOtherEntity (with generated value) in a one or many relationship.
I thus have the following error :
ERROR: insert or update on table "t_other_entity" violates foreign key constraint "other_entity_entity"
Détail : Key (entity_id)=(110) is not present in table "t_entity"
Seems legit since the insert has not been sent for Entity, but why ? Again, if I change the ID of the Entity and use #GeneratedValue I don't get any error.
I'm using Spring Boot 1.5.12, Java 8 and PostgreSQL 9
You're basically switching from automatically assigned identifiers to manually defined ones which has a couple of consequences both on the JPA and Spring Data level.
Database operation timing
On the plain JPA level, the persistence provider doesn't necessarily need to immediately execute a single insert as it doesn't have to obtain an identifier value. That's why it usually delays the execution of the statement until it needs to flush, which is on either an explicit call to EntityManager.flush(), a query execution as that requires the data in the database to be up to date to deliver correct results or transaction commit.
Spring Data JPA repositories automatically use default transactions on the call to save(…). However, if you're calling repositories within a method annotated with #Transactional in turn, the databse interaction might not occur until that method is left.
EntityManager.persist(…) VS. ….merge(…)
JPA requires the EntityManager client code to differentiate between persisting a completely new entity or applying changes to an existing one. Spring Data repositories w ant to free the client code from having to deal with this distinction as business code shouldn't be overloaded with that implementation detail. That means, Spring Data will somehow have to differentiate new entities from existing ones itself. The various strategies are described in the reference documentation.
In case of manually identifiers the default of inspecting the identifier property for null values will not work as the property will never be null by definition. A standard pattern is to tweak the entities to implement Persistable and keep a transient is-new-flag around and use entity callback annotations to flip the flag.
#MappedSuperclass
public abstract class AbstractEntity<ID extends SalespointIdentifier> implements Persistable<ID> {
private #Transient boolean isNew = true;
#Override
public boolean isNew() {
return isNew;
}
#PrePersist
#PostLoad
void markNotNew() {
this.isNew = false;
}
// More code…
}
isNew is declared transient so that it doesn't get persisted. The type implements Persistable so that the Spring Data JPA implementation of the repository's save(…) method will use that. The code above results in entities created from user code using new having the flag set to true, but any kind of database interaction (saving or loading) turning the entity into a existing one, so that save(…) will trigger EntityManager.persist(…) initially but ….merge(…) for all subsequent operations.
I took the chance to create DATAJPA-1600 and added a summary of this description to the reference docs.
Given two entities like so
#Entity
public class Thing {
#ManyToOne
private ThingType thingType;
...
}
#Entity
public class ThingType {
private String name;
...
}
From everything I have read, the default cascade should be nothing so if I get a reference to a Thing thing, and change the name field of its ThingType, then using a JpaRepository<Thing, Long> call thingRepo.save(thing), I would expect the change to ThingTypes name not to be persisted.
However this is not the case and the change is persisted. I am not sure why this is happening? What I am missing here?
Relevant versions:
org.springframework.boot:spring-boot:jar:1.5.7.RELEASE
org.hibernate:hibernate-core:jar:5.0.12.Final
org.springframework.data:spring-data-jpa:jar:1.11.7.RELEASE
Well, I would have expected the same, but it seems that Hibernate has its own default behaviour. In the Hibernate forum someone asked almost the same question. The answer refers to the Hibernate "dirty check" feature, which will detect and perstst/merge the change. You might change that behaviour by using Hibernate's Cascade annotation.
Well cascading is something else, let me ask you something. Do following things:
Thing thing = session.get(Thing .class, someId);
thing.getThingType().setTitle("new title");
and nothing more, again you see hibernate updates thingType.
It is called dirty checking, as long as an entity is attached to an active hibernate session, and its persistence state changes hibernate automatically updates its associated row in database. Event without calling a save or update.
So what is cascade?
Consider following case:
Thing myThing = new Thing();
ThingType myThingType = new ThingType();
myThing.setThingType(myThingType);
session.save(myThing);
if the association cascade type is not set, then you will get an exception, because you are referencing a transient thingType object. But if you set the cascade type to persist, then hibernate first saves the thingType and then saves the thing, and everything goes fine.
So remember, if you fetch an object, then update its properties in the same session, there is no need to call a update or saveOrUpdate method on hibernate session (or jpa entityManager) because it is already in attached state, and its state is traced by hibernate.
We've got a Spring project that is using the AuditingEntityListener on our JPA entities:
#EntityListeners(AuditingEntityListener.class)
Our base entity has a lastModifiedDate defined as:
#Column(name = "modified_time")
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
This value is being set automatically when the entity is saved or updated - which is how we'd like the application to behave. We run into issues, though, when we try to set up data in our test suites because in some situations (not all), we'd like to bypass the automatic setting of this field and set the value ourself. In this specific situation, we're trying to order a bunch of test data and then run a test against it.
Is there any way to bypass or turn off the AuditingEntityListener in order to create test data?
Declaring
#MockBean
private AuditingHandler auditingHandler
in your test should prevent the #LastModifiedDate from having any effect.
I can imagine the following solution: create two persistence.xml files - one for production and another for testing purposes:
the production related persistence.xml includes reference to orm_production.xml mapping file that specifies AuditingEntityListener with entity-listener attribute
the tests related persistence.xml may include reference to orm_test.xml maping file that specifies AuditingEntityListener with entity-listener attribute. Additionally, your base entity needs to be defined entirely within xml mapping file and specify:
metadata-complete attribute: tells the provider to ignore in-code annotations
exclude-default-listeners attribute: tells the provider to ignore entity listeners but only for the corresponding base entity
Does an equivalent for the Hibernate filters exist in the JPA?
The following hibernate annotation can be for example used in order to define a filter:
#Entity
#FilterDef(name="minLength", parameters=#ParamDef( name="minLength", type="integer" ) )
#Filters( {
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
#Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
I would like to use something equivalent from JPA in order to restrict read access to some entities. How it can be done using clean JPA, without Hibernate annotations?
I didn't find any serious and reliable solution.
I analysed the "JPA Security" project. However, its home page was last updated two years ago, its last version is 0.4.0 and it doesn't seem to be a reliable solution. It's not a standard and it is not popular.
Other alternative in Hibernate which can be used in my case to restrict read access to an entity is the Hibernate Interceptor API - the following interface method can be implemented in order to append a SQL string which contains some additional conditions:
org.hibernate.Interceptor.onPrepareStatement(String sql)
or the following method can be overriden:
org.hibernate.EmptyInterceptor.onPrepareStatement(String sql)
I found out that there are some JPA event callbacks and annotations, e.g. #PostLoad. However, none of these can be used in my case, because I need something to restrict access to entities based on some conditions (user role).
Anyone knows how it can be done using JPA standards?
It seems to me that you are attempting to perform validations on entity objects. You have a few options to accomplish this.
The first would be to use the Java Validations API and its associated validations. This is the recommended approach with Java EE, of which JPA is a part. For example, you could write your entity class as follows:
#Entity
class Person {
#NotNull
#Size(min = 5, Max = 50)
String name;
}
Now, every time you attempt to persist an instance of Person, the JPA provider will automatically validate the instance, provided there is a Java Validator on the classpath. Validation errors will be thrown as runtime exceptions and can be used to rollback transactions. It is also possible to invoke a validator manually, collect any validation errors and transform them into user-friendly messages if required.
The other (probably dirty) option is to use the JPA Event Listeners, perform validations and throw an exception if a validation fails. This will terminate the JPA operation immediately and rollback any transactions.
Is it possible to stop hibernate from auto updating a persistent object?
#Transactional
public ResultTO updateRecord(RequestTO requestTO) {
Entity entity = dao.getEntityById(requestTO.getId());
// now update the entity based on the data in the requestTO
ValidationResult validationResult = runValidation(entity);
if(validationResult.hasErrors()) {
// return ResultTO with validation errors
} else {
dao.persist(entity);
}
}
Here is what happens in the code, I retrieve the entity which would be considered by hibernate to be in persistent state, then I update some of the fields in the entity, then pass the entity to validation. if validation fails, then don't udpate, if validation succeeds then persist the entity.
Here is the main issue with this flow: because I updated the entity for it to be used in the validation, it does not matter whether I call persist() method (on the DAO) or not, the record will always be updated because hibernate detects that the entity has been changed and flags it for update.
Keep im mind I can change the way i do validation and work around the issue, so I'm not interested in workarounds. I'm interested in knowing how i would be able to disable the hibernate feature where it automatically updates persistent objects.
Please keep in mind I'm using hibernates' implementation of JPA. so Hibernate specific answers dealing with hibernate specific API will not work for me.
I tried to look for hibernate configuration and see if I can set any configuration to stop this behavior but no luck.
Thanks
--EDIT ---
I couldn't find a solution to this, so I opted to rolling back the transaction without throwing any RuntimeException even though I'm in a declarative transaction using:
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
which works like a charm.
Configure FlushMode for your session.
http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/FlushMode.html
You can use EntityManager.clear() method after getting object from database.
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#clear()
You can call the following code:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Throw an exception if validation fails and have the caller handle that.