How does Spring Data jpa know properties changed? - java

So say I lookup an object from the repository. If I save this object immediately after lookup, Spring Data is smart enough not to update the database. If I change a property within this object and then save, spring data does an update. How does it know it needs to do an update or not?

This is not provided by Spring Data, its a feature of your persistence framework (hibernate, openjpa, eclipselink,...).
Persistence providers enhance the domain objects with some "stuff" for optimization. Normally, this is done by so called runtime enhancement, so your class gets loaded inside of the application and enhanced there(runtime weaving).
Openjpa also allows build-time-enhancement, which means, the "openjpa-domain-extension-stuff" becomes added to your entities at compile time. (there is a maven goal in the openjpa plugin too)
https://openjpa.apache.org/builds/2.2.2/apache-openjpa/docs/ref_guide_pc_enhance.html
If you run mvn openjpa:enhance your simple domain will look now like the following:
(I used jad to decompile the class, as it is to long to show all stuff inside, I copied the most relevant parts)
import org.apache.openjpa.enhance.*;
import org.apache.openjpa.util.IntId;
import org.apache.openjpa.util.InternalException;
public class Entity implements PersistenceCapable
{
public Integer getId()
{
return pcGetid(this);
}
public void setId(Integer id)
{
pcSetid(this, id);
}
....
....
private static final void pcSetid(Entity entity, Integer integer)
{
if(entity.pcStateManager == null)
{
entity.id = integer;
return;
} else
{
entity.pcStateManager.settingObjectField(entity, pcInheritedFieldCount + 3, entity.id, integer, 0);
return;
}
}
....
protected void pcClearFields()
{
id = null;
}
public PersistenceCapable pcNewInstance(StateManager statemanager, Object obj, boolean flag)
{
Entity entity = new Entity();
if(flag)
entity.pcClearFields();
entity.pcStateManager = statemanager;
entity.pcCopyKeyFieldsFromObjectId(obj);
return entity;
}
}
By manipulating your entity, the pcStateManager gets invoked. If you run a persist operation, the persistence framework checks the statemanager if there are changes within your entity and sends the update to the database if necessary.

Spring doesn't actually work directly on instances of your class. What it does is create a proxy that wraps the actual instance and delegates to it. This proxy holds the state of persistence of the underlying instance. In other words, it knows if the instance is in the same state as it is in the database as it is in memory.
If you invoke (certain) methods, it will consider itself dirty. The EntityManager will have to push those changes. If you don't, then it also knows that no changes need to be pushed.

Related

How can I tell if current session is dirty?

I want to publish an event if and only if there were changes to the DB. I'm running under #Transaction is Spring context and I come up with this check:
Session session = entityManager.unwrap(Session.class);
session.isDirty();
That seems to fail for new (Transient) objects:
#Transactional
public Entity save(Entity newEntity) {
Entity entity = entityRepository.save(newEntity);
Session session = entityManager.unwrap(Session.class);
session.isDirty(); // <-- returns `false` ):
return entity;
}
Based on the answer here https://stackoverflow.com/a/5268617/672689 I would expect it to work and return true.
What am I missing?
UPDATE
Considering #fladdimir answer, although this function is called in a transaction context, I did add the #Transactional (from org.springframework.transaction.annotation) on the function. but I still encounter the same behaviour. The isDirty is returning false.
Moreover, as expected, the new entity doesn't shows on the DB while the program is hold on breakpoint at the line of the session.isDirty().
UPDATE_2
I also tried to change the session flush modes before calling the repo save, also without any effect:
session.setFlushMode(FlushModeType.COMMIT);
session.setHibernateFlushMode(FlushMode.MANUAL);
First of all, Session.isDirty() has a different meaning than what I understood. It tells if the current session is holding in memory queries which still haven't been sent to the DB. While I thought it tells if the transaction have changing queries. When saving a new entity, even in transaction, the insert query must be sent to the DB in order to get the new entity id, therefore the isDirty() will always be false after it.
So I ended up creating a class to extend SessionImpl and hold the change status for the session, updating it on persist and merge calls (the functions hibernate is using)
So this is the class I wrote:
import org.hibernate.HibernateException;
import org.hibernate.internal.SessionCreationOptions;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.SessionImpl;
public class CustomSession extends SessionImpl {
private boolean changed;
public CustomSession(SessionFactoryImpl factory, SessionCreationOptions options) {
super(factory, options);
changed = false;
}
#Override
public void persist(Object object) throws HibernateException {
super.persist(object);
changed = true;
}
#Override
public void flush() throws HibernateException {
changed = changed || isDirty();
super.flush();
}
public boolean isChanged() {
return changed || isDirty();
}
}
In order to use it I had to:
extend SessionFactoryImpl.SessionBuilderImpl to override the openSession function and return my CustomSession
extend SessionFactoryImpl to override the withOptions function to return the extended SessionFactoryImpl.SessionBuilderImpl
extend AbstractDelegatingSessionFactoryBuilderImplementor to override the build function to return the extended SessionFactoryImpl
implement SessionFactoryBuilderFactory to implement getSessionFactoryBuilder to return the extended AbstractDelegatingSessionFactoryBuilderImplementor
add org.hibernate.boot.spi.SessionFactoryBuilderFactory file under META-INF/services with value of my SessionFactoryBuilderFactory implementation full class name (for the spring to be aware of it).
UPDATE
There was a bug with capturing the "merge" calls (as tremendous7 comment), so I end up capturing the isDirty state before any flush, and also checking it once more when checking isChanged()
The following is a different way you might be able to leverage to track dirtiness.
Though architecturally different than your sample code, it may be more to the point of your actual goal (I want to publish an event if and only if there were changes to the DB).
Maybe you could use an Interceptor listener to let the entity manager do the heavy lifting and just TELL you what's dirty. Then you only have to react to it, instead of prod it to sort out what's dirty in the first place.
Take a look at this article: https://www.baeldung.com/hibernate-entity-lifecycle
It has a lot of test cases that basically check for dirtiness of objects being saved in various contexts and then it relies on a piece of code called the DirtyDataInspector that effectively listens to any items that are flagged dirty on flush and then just remembers them (i.e. keeps them in a list) so the unit test cases can assert that the things that SHOULD have been dirty were actually flushed as dirty.
The dirty data inspector code is on their github. Here's the direct link for ease of access.
Here is the code where the interceptor is applied to the factory so it can be effective. You might need to write this up in your injection framework accordingly.
The code for the Interceptor it is based on has a TON of lifecycle methods you can probably exploit to get the perfect behavior for "do this if there was actually a dirty save that occured".
You can see the full docs of it here.
We do not know your complete setup, but as #Christian Beikov suggested in the comment, is it possible that the insertion was already flushed before you call isDirty()?
This would happen when you called repository.save(newEntity) without a running transaction, since the SimpleJpaRepository's save method is annotated itself with #Transactional:
#Transactional
#Override
public <S extends T> S save(S entity) {
...
}
This will wrap the call in a new transaction if none is already active, and flush the insertion to the DB at the end of the transaction just before the method returns.
You might choose to annotate the method where you call save and isDirty with #Transactional, so that the transaction is created when your method is called, and propagated to the repository call. This way the transaction would not be committed when the save returns, and the session would still be dirty.
(edit, just for completeness: in case of using an identity ID generation strategy, the insertion of newly created entity is flushed during a repository's save call to generate the ID, before the running transaction is committed)

Clean Architecture and Cache Invalidation

I have an app that tries to follow the Clean Architecture and I need to do some cache invalidation but I don't know in which layer this should be done.
For the sake of this example, let's say I have an OrderInteractor with 2 use cases : getOrderHistory() and sendOrder(Order).
The first use case is using an OrderHistoryRepository and the second one is using a OrderSenderRepository. Theses repositories are interfaces with multiple implementations (MockOrderHistoryRepository and InternetOrderHistoryRepository for the first one). The OrderInteractor only interact with theses repositories through the interfaces in order to hide the real implementation.
The Mock version is very dummy but the Internet version of the history repository is keeping some data in cache to perform better.
Now, I want to implement the following : when an order is sent successfully, I want to invalidate the cache of the history but I don't know where exactly I should perform the actual cache invalidation.
My first guess is to add a invalidateCache() to the OrderHistoryRepository and use this method at the end of the sendOrder() method inside the interactor. In the InternetOrderHistoryRepository, I will just have to implement the cache invalidation and I will be good. But I will be forced to actually implement the method inside the MockOrderHistoryRepository and it's exposing to the outside the fact that some cache management is performed by the repository. I think that the OrderInteractor should not be aware of this cache management because it is implementation details of the Internet version of the OrderHistoryRepository.
My second guess would be perform the cache invalidation inside the InternetOrderSenderRepository when it knows that the order was sent successfully but it will force this repository to know the InternetOrderHistoryRepository in order to get the cache key used by this repo for the cache management. And I don't want my OrderSenderRepository to have a dependency with the OrderHistoryRepository.
Finally, my third guess is to have some sort of CacheInvalidator (whatever the name) interface with a Dummy implementation used when the repository is mocked and an Real implementation when the Interactor is using the Internet repositories. This CacheInvalidator would be injected to the Interactor and the selected implementation would be provided by a Factory that's building the repository and the CacheInvalidator. This means that I will have a MockedOrderHistoryRepositoryFactory - that's building the MockedOrderHistoryRepository and the DummyCacheInvalidator - and a InternetOrderHistoryRepositoryFactory - that's building the InternetOrderHistoryRepository and the RealCacheInvalidator. But here again, I don't know if this CacheInvalidator should be used by the Interactor at the end of sendOrder() or directly by the InternetOrderSenderRepository (even though I think the latter is better because again the interactor should probably not know that there is some cache management under the hood).
What would be your preferred way of architecturing this ?
Thank you very much.
Pierre
Your 2nd guess is correct because caching is a detail of the persistence mechanism. E.g. if the repository would be a file based repository caching might not be an issue (e.g. a local ssd).
The interactor (use case) should not know about caching at all. This will make it easier to test because you don't need a real cache or mock for testing.
My second guess would be perform the cache invalidation inside the InternetOrderSenderRepository when it knows that the order was sent successfully but it will force this repository to know the InternetOrderHistoryRepository in order to get the cache key used by this repo for the cache management.
It seems that your cache key is a composite of multiple order properties and therefore you need to encapsulate the cache key creation logic somewhere for reuse.
In this case, you have the following options:
One implementation for both interfaces
You can create a class that implements the InternetOrderSenderRepository as well as the InternetOrderHistoryRepository interface. In this case, you can extract the cache key generation logic into a private method and reuse it.
Use a utility class for the cache key creation
Simple extract the cache key creation logic in a utility class and use it in both repositories.
Create a cache key class
A cache key is just an arbitrary object because a cache must only check if a key exists and this means use the equals method that every object has. But to be more type-safe most caches use a generic type for the key so that you can define one.
Thus you can put the cache key logic and validation in an own class. This has the advantage that you can easily test that logic.
public class OrderCacheKey {
private Integer orderId;
private int version;
public OrderCacheKey(Integer orderId, int version) {
this.orderId = Objects.requireNonNull(orderId);
if (version < 0) {
throw new IllegalArgumentException("version must be a positive integer");
}
this.version = version;
}
public OrderCacheKey(Order order) {
this(order.getId(), order.getVersion());
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
OrderCacheKey other = (OrderCacheKey) obj;
if (!Objects.equals(orderId, other.orderId))
return false;
return Objects.equals(version, other.version);
}
public int hashCode() {
int result = 1;
result = 31 * result + Objects.hashCode(orderId);
result = 31 * result + Objects.hashCode(version);
return result;
}
}
You can use this class as the key type of your cache: Cache<OrderCacheKey, Order>. Then you can use the OrderCacheKey class in both repository implementations.
Introduce a order cache interface to hide caching details
You can apply the interface segregation principle and hide the complete caching details behind a simple interface. This will make your unit tests more easy because you have to mock less.
public interface OrderCache {
public void add(Order order);
public Order get(Integer orderId, int version);
public void remove(Order order);
public void removeByKey(Integer orderId, int version);
}
You can then use the OrderCache in both repository implementations and you can also combine the interface segregation with the cache key class above.
How to apply
You can use aspect-oriented programming and one of the options above to implement the caching
You can create a wrapper (or delegate) for each repository that applies caching and delegates to the real repositories when needed. This is very similar to the aspect-oriented way. You just implement the aspect manually.

Ways to pass additional data to Custom RevisionEntity in Hibernate Envers?

It's RESTful web app. I am using Hibernate Envers to store historical data. Along with revision number and timestamp, I also need to store other details (for example: IP address and authenticated user). Envers provides multiple ways to have a custom revision entity which is awesome. I am facing problem in setting the custom data on the revision entity.
#RevisionEntity( MyCustomRevisionListener.class )
public class MyCustomRevisionEntity extends DefaultRevisionEntity {
private String userName;
private String ip;
//Accessors
}
public class MyCustomRevisionListener implements RevisionListener {
public void newRevision( Object revisionEntity ) {
MyCustomRevisionEntity customRevisionEntity = ( MyCustomRevisionEntity ) revisionEntity;
//Here I need userName and Ip address passed as arguments somehow, so that I can set them on the revision entity.
}
}
Since newRevision() method does not allow any additional arguments, I can not pass my custom data (like username and ip) to it. How can I do that?
Envers also provides another approach as:
An alternative method to using the org.hibernate.envers.RevisionListener is to instead call the getCurrentRevision( Class revisionEntityClass, boolean persist ) method of the org.hibernate.envers.AuditReader interface to obtain the current revision, and fill it with desired information.
So using the above approach, I'll have to do something like this:
Change my current dao method like:
public void persist(SomeEntity entity) {
...
entityManager.persist(entity);
...
}
to
public void persist(SomeEntity entity, String userName, String ip) {
...
//Do the intended work
entityManager.persist(entity);
//Do the additional work
AuditReader reader = AuditReaderFactory.get(entityManager)
MyCustomRevisionEntity revision = reader.getCurrentRevision(MyCustomRevisionEntity, false);
revision.setUserName(userName);
revision.setIp(ip);
}
I don't feel very comfortable with this approach as keeping audit data seems a cross cutting concern to me. And I obtain the userName and Ip and other data through HTTP request object. So all that data will need to flow down right from entry point of application (controller) to the lowest layer (dao layer).
Is there any other way in which I can achieve this? I am using Spring.
I am imagining something like Spring keeping information about the 'stack' to which a particular method invocation belongs. So that when newRevision() in invoked, I know which particular invocation at the entry point lead to this invocation. And also, I can somehow obtain the arguments passed to first method of the call stack.
One good way to do this would be to leverage a ThreadLocal variable.
As an example, Spring Security has a filter that initializes a thread local variable stored in SecurityContextHolder and then you can access this data from that specific thread simply by doing something like:
SecurityContext ctx = SecurityContextHolder.getSecurityContext();
Authorization authorization = ctx.getAuthorization();
So imagine an additional interceptor that your web framework calls that either adds additional information to the spring security context, perhaps in a custom user details object if using spring security or create your own holder & context object to hold the information the listener needs.
Then it becomes a simple:
public class MyRevisionEntityListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
// If you use spring security, you could use SpringSecurityContextHolder.
final UserContext userContext = UserContextHolder.getUserContext();
MyRevisionEntity mre = MyRevisionEntity.class.cast( revisionEntity );
mre.setIpAddress( userContext.getIpAddress() );
mre.setUserName( userContext.getUserName() );
}
}
This feels like the cleanest approach to me.
It is worth noting that the other API getCurrentRevision(Session,boolean) was deprecated as of Hibernate 5.2 and is scheduled for removal in 6.0. While an alternative means may be introduced, the intended way to perform this type of logic is using a RevisionListener.

How to update persisting state of intellij idea plugin in test class?

I'm developing plugin for Intellij Idea and doing some tests. In one of my tests I need persisting state to be updated. But it doesn't happens and neither loadState nor getState is called.
I wrote class that implements PersistingStateComponent (i think I did it correctly because it's not the first time I did it). In one test I call method that adds data to State class, and it's successfully added but not saved. Another one should get that data, but it gets empty State. Test class implements LightPlatformTestCase.
Documentation says :
Persistent Component Lifecycle The loadState() method is called after
the component has been created (only if there is some non-default
state persisted for the component), and after the XML file with the
persisted state is changed externally (for example, if the project
file was updated from the version control system). In the latter case,
the component is responsible for updating the UI and other related
components according to the changed state.
The getState() method is called every time the settings are saved (for
example, on frame deactivation or when closing the IDE). If the state
returned from getState() is equal to the default state (obtained by
creating the state class with a default constructor), nothing is
persisted in the XML. Otherwise, the returned state is serialized in
XML and stored.
So is it possible that none of these conditions happen?
Can I do something in test method to update my persisting state?
Or it supposed to work and I should look for issue in my code?
Update: When I run plugin it works fine.
My class looks like that:
#State(name = "MyStateName", storages = {#Storage(id="MyStateId", file = "D:/MyStateName.xml")})
public class MyClass
implements PersistentStateComponent<MyClass.State> {
public static class State{
Integer someValue = 10;
}
State myState = new State();
public State getState() {
return myState;
}
public void loadState(State state) {
myState = state;
}
public SFApexClassWrapp getValue() {
return myState.value;
}
public void addValue(Integer value) {
myState.value = value;
}
}
When you're using PersistentStateComponent, your only responsibility as a plugin developer is to return an instance of the State class. Therefore, the only thing that it makes sense to test for you as a plugin developer is that your instance is returned correctly. You don't need to test the XML serialization, because it's part of IntelliJ IDEA's code, which is not your responsibility and is already reasonably well-tested.
That's why, when IntelliJ IDEA runs tests for your code, it does not save or load settings for your components. You can trigger the settings saving manually if you need, but in general it's not necessary for you to do that.
Please post a separate question regarding storing multiple instances of your configuration objects.

Restrict access to the owner of an object in DDD

Let's say there is an object TaskList which can be edited and deleted only by its owner. Other users should only by able to take a task and update its status.
The following options come to my mind:
check the ownership and access in the controller of the web application
let the repository return proxy object which throws exception on certain operations, but the controller (or view) would still need to know which actions (in form of links or form fields) should be visible
pass the caller (user) to the method of the domain object, so that the domain object can itself check whether the caller ist allowed or not.
The used technology is Java.
Any other/better ideas?
Interesting articles about security and DDD
Domain Object Security with the Spring framework
Security in Domain-Driven Design
I have accepted my own answer now, because that is what I actually use, but further suggestions are welcome.
I would not encode the ownership/permissions model into the TaskList domain object. That sort of business logic should be external. I also don't like the idea of a proxy object. Although it would certainly work, it would confuse debugging and is, in this case at least, unnecessarily complex. I would also not check it in the controller.
Instead I would create a business logic object which oversees the permissions for TaskList. So the TaskList would have an owner field but you would have something like:
public class TaskListAccessor {
private TaskList taskList;
private User reader;
public void updateStatus(Status status) {
// everyone can do this
taskList.updateStatus(status);
}
/** Return true if delete operation is allowed else false */
public boolean isDeleteAllowed() {
return taskList.getOwner().equals(reader);
}
/** Delete the task. Only owners can do this. Returns true if worked else false */
public boolean delete() {
if (isDeleteAllowed()) {
taskList.delete();
return true;
} else {
return false;
}
}
// ... other accessors with other is*Allowed methods
}
If you need to require that all operations on TaskList objects go through accessors then you could create a factory class which is the only one who creates TaskList using package constructors or something. Maybe the factory is the only one who would use the DAO to look up the TaskList from the data store.
However, if there are too many methods to control in this fashion then a proxy might be easier. In both cases having TaskList be an interface would be recommended, with the implementation class hidden by the proxy or the accessor.
I found it unnecessarily complex to create accessor classes for each protected domain class as suggested by 'Gray'. My solution is probably not perfect, but simple to use and - more important - robust. You cannot forget to use a certain object or to check conditions outside.
public class TaskList {
private SystemUser owner;
private List<Task> tasks = new ArrayList<>();
public TastList(SystemUser owner) {
this.owner = owner;
}
public void Add(Task task) {
Guard.allowFor(owner);
tasks.add(task);
}
}
The Guard knows the current user (from a thread local for example) and compares it to the owner passed as parameter to allowFor(owner). If access is denied a security exception will be thrown.
That is simple, robust and even easy to maintain since only the guard has to be changed if the underlying authentication changes.

Categories

Resources