Spring log if cache is called for #Cacheable - java

I have a simple spring 3.2 web application, which is connected to a MySQL db. My question is simple: I have method in dao, which is annotated with #Cacheable. Is there a way to log if the method goes to db, or its result is loaded from cache? For example, I'd like to see the following log:
Object with id 'x' was retrieved from database at 23:44:30 / 2015....
Object with id 'x' was retrived from cache at...
Thank you

Spring logs out cache hits and misses under the org.springframework.cache category at TRACE level.

you can a log message in your service layer if there's going to be a call to your persistence layer. here's a method from some code i'm working where i'm doing this. I only get a log entry when the cache isn't hit.
#Cacheable(value = CACHE_PICO)
#Transactional(readOnly = true)
#PostAuthorize(PICO_READ + OR + ALLOWED_FOR_ADMIN)
public Pico get(long id) {
log.info("cached missed for pico {}", id);
return _picoRepository.findOne(id);
}

You could use log4jdbc (docu) - it is wrapper around your normal jdbc connection that logs every executed statement.

Related

Spring boot #Cacheble with Ehcache

I'm using Spring boot with Ehcache for caching some data in the application.
The application is a rest service that caches some data that has high usage.
The code in our controllers looks like:
#Cacheable("CategoryModels")
#GetMapping("/category/{companyId}")
public List<CategoryViewModel> getAllCategories(#PathVariable(value = "companyId", required = true) long companyId,
#RequestHeader("user") String user) {
//custom code here
}
Now in some situations the users are getting different data sets back from the server. Can someone explain this in the above situation?
If data is changed in the database I refresh the cache and the program will auto update the updated data to the
For refreshing the cache I use a custom written method:
Cache categoryCache = (Cache) manager.getCache("CategoryModels").getNativeCache();
categoryCache.removeAll();
categoryController.getAllCategories(company.getCompanyId(), null);
I have the same behavior on other caches that are used and refreshed on the same way the above cache is used.
You should try to parametrize your cache definition with :
#Cacheable(value="CategoryModels", key="{ #root.methodName, #companyId, #user.id }")
It may be a couple of things. First off the default key resolver that spring provides does not consider anything but the names of the parameters. The cleanest way to fix this kid to write your own key revolver that considers both class and method, without this it could be possible to get back data from a completely different method that happens to share the same parameter list.

Spring Data Jpa - cache

Hello my problem is that i can't avoid cache. I'm using Spring Data Jpa with Spring Boot 1.5.4
What am I doing:
I have a case when some client request my REST endpoint with some data based on which I am creating an entity and I'm saveing it into database, next I request another REST endpoint which response me dirlectly OK, but request which I got isn't finished yet. Next I'm waiting for another service which has to request my another REST endpoint (First client is all the time on wire). This endpoint modifies entity which was created after first request I got and here I got problem.
So basicly, first request creates entity and saves it using "saveAndFlush" method. When first request is waiting another thread modifies this entity using spring data jpa:
#Modifying(clearAutomatically = true)
#Query("UPDATE QUERY ")
#Transactional
int updateMethod();
But after that (when first request is released from waiting) when I call findOne method in first thread I got old entity, I have tried also override method:
#Transactional
default MyEntity findOneWithoutCache(Long id) {
return findOne(id);
}
But this not working too, I also added
#Cacheable(false)
public class MyEntity {
And this is not working too.
There is only one way which is working, when i select this entity using #Query this way:
#Query("SELECT STATEMENT "
+ "WHERE p.id = ?1")
MyEntity findEntityById(Long id);
Could you explain me how to solve this problem?
The thing is what kind of Transaction Isolation do You have?
What Database , settings, driver?
Theoretically in a perfect ACID transaction - after starting transaction you cannot see changes done in other transactions. (see Repeatable Read).
On the other hand typically you do not have I in ACID. And isolation is weaker.
(like Read Commited).
If the query works it suggests you do not have Repeatable read - so maybe you should simply get EnityManager (via JpaContext) and try to clear() session (in the 1st thread)?

Spring #Transaction (readonly=true) context commit data to DB in the middle of the process

I am working on an assignment to make the code transactional. I am having this problem about read-only transaction for while and none of a single suggestion I found on the internet didn't work. (Spring and hibernate integrated project)
This is my read-only transactional method
#Transactional(propagation=Propagation.REQUIRES_NEW, readOnly=true
,rollbackFor=Exception.class)
public void
editInternationalExportConsigment(InternationalExportConsignmentFormWrapper
exportConssi (){}
Inside this method, there is a translator process happening. Where the process fetch (select ) data from DB and set to an Object
Contact contact =inquiry.loadCustomerContactById(consignmentVO.getCustomerContactId().intValue());
if (contact != null && contact.getCity() != null) {
consignment.setOrgin(contact.getCity().getCountry());
consignment.setUniqueOriginCountry((contact.getCity().getCountry()!=null)?contact.getCity().getCountry().getId():null);
consignment.setOrginCity(contact.getCity());
}
There are no any update or insert query run in the middle, Only select. But end of the this code snippet execution it commit the data to DB (whatever the value set to setter method will persist into DB )
Can someone please tell me what wrong is happening here. your feedback will be much appricated.
After tedious research, I have found the answer. In our project there are two session factories are running. And also it uses spring OpenSessionInViewFilter to avoid 'lazy initializing' issue. OpenSessionInViewFilter has set flushMode to Auto. Since the OpenSessionInViewFilter keeping binding a hibernate session to the thread along in the entire process, it will override the transactional hibernate session object which gets creates once I start the new transaction.Therefore even if I kept the flushmode for transactional scope as 'COMMIT' it get override with the AUTO property by the properties declared OpenSessionInViewFilter.
When the flushMode is AUTO hibernate will flush dirty objects to DB.
Read this for understand hibernate data flushin
As a solution, I manually change the flush mode to 'COMMIT' inside my transactional method.
Thank you for everyone replied and commented. :)

How can I use Hibernate/JPA to tell the DB who the user is before inserts/updates/deletes?

Summary (details below):
I'd like to make a stored proc call before any entities are saved/updated/deleted using a Spring/JPA stack.
Boring details:
We have an Oracle/JPA(Hibernate)/Spring MVC (with Spring Data repos) application that is set up to use triggers to record history of some tables into a set of history tables (one history table per table we want audited). Each of these entities has a modifiedByUser being set via a class that extends EmptyInterceptor on update or insert. When the trigger archives any insert or update, it can easily see who made the change using this column (we're interested in which application user, not database user). The problem is that for deletes, we won't get the last modified information from the SQL that is executed because it's just a plain delete from x where y.
To solve this, we'd like to execute a stored procedure to tell the database which app user is logged in before executing any operation. The audit trigger would then look at this value when a delete happens and use it to record who executed the delete.
Is there any way to intercept the begin transaction or some other way to execute SQL or a stored procedure to tell the db what user is executing the inserts/updates/deletes that are about to happen in the transaction before the rest of the operations happen?
I'm light on details about how the database side will work but can get more if necessary. The gist is that the stored proc will create a context that will hold session variables and the trigger will query that context on delete to get the user ID.
From the database end, there is some discussion on this here:
https://docs.oracle.com/cd/B19306_01/network.102/b14266/apdvprxy.htm#i1010372
Many applications use session pooling to set up a number of sessions
to be reused by multiple application users. Users authenticate
themselves to a middle-tier application, which uses a single identity
to log in to the database and maintains all the user connections. In
this model, application users are users who are authenticated to the
middle tier of an application, but who are not known to the
database.....in these situations, the application typically connects
as a single database user and all actions are taken as that user.
Because all user sessions are created as the same user, this security
model makes it very difficult to achieve data separation for each
user. These applications can use the CLIENT_IDENTIFIER attribute to
preserve the real application user identity through to the database.
From the Spring/JPA side of things see section 8.2 at the below:
http://docs.spring.io/spring-data/jdbc/docs/current/reference/html/orcl.connection.html
There are times when you want to prepare the database connection in
certain ways that aren't easily supported using standard connection
properties. One example would be to set certain session properties in
the SYS_CONTEXT like MODULE or CLIENT_IDENTIFIER. This chapter
explains how to use a ConnectionPreparer to accomplish this. The
example will set the CLIENT_IDENTIFIER.
The example given in the Spring docs uses XML config. If you are using Java config then it looks like:
#Component
#Aspect
public class ClientIdentifierConnectionPreparer implements ConnectionPreparer
{
#AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
public Connection prepare(Connection connection) throws SQLException
{
String webAppUser = //from Spring Security Context or wherever;
CallableStatement cs = connection.prepareCall(
"{ call DBMS_SESSION.SET_IDENTIFIER(?) }");
cs.setString(1, webAppUser);
cs.execute();
cs.close();
return connection;
}
}
Enable AspectJ via a Configuration class:
#Configuration
#EnableAspectJAutoProxy
public class SomeConfigurationClass
{
}
Note that while this is hidden away in a section specific to Spring's Oracle extensions it seems to me that there is nothing in section 8.2 (unlike 8.1) that is Oracle specific (other than the Statement executed) and the general approach should be feasible with any Database simply by specifying the relevant procedure call or SQL:
Postgres for example as the following so I don't see why anyone using Postgres couldn't use this approach with the below:
https://www.postgresql.org/docs/8.4/static/sql-set-role.html
Unless your stored procedure does more than what you described, the cleaner solution is to use Envers (Entity Versioning). Hibernate can automatically store the versions of an entity in a separate table and keep track of all the CRUD operations for you, and you don't have to worry about failed transactions since this will all happen within the same session.
As for keeping track who made the change, add a new colulmn (updatedBy) and just get the login ID of the user from Security Principal (e.g. Spring Security User)
Also check out #CreationTimestamp and #UpdateTimestamp.
I think what you are looking for is a TransactionalEvent:
#Service
public class TransactionalListenerService{
#Autowired
SessionFactory sessionFactory;
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleEntityCreationEvent(CreationEvent<Entity> creationEvent) {
// use sessionFactory to run a stored procedure
}
}
Registering a regular event listener is done via the #EventListener
annotation. If you need to bind it to the transaction use
#TransactionalEventListener. When you do so, the listener will be
bound to the commit phase of the transaction by default.
Then in your transactional services you register the event where necessary:
#Service
public class MyTransactionalService{
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Transactional
public void insertEntityMethod(Entity entity){
// insert
// Publish event after insert operation
applicationEventPublisher.publishEvent(new CreationEvent(this, entity));
// more processing
}
}
This can work also outside the boundaries of a trasaction if you have the requirement:
If no transaction is running, the listener is not invoked at all since
we can’t honor the required semantics. It is however possible to
override that behaviour by setting the fallbackExecution attribute of
the annotation to true.

Should a Shiro Authenticating Realm be transactional?

I'm using Shiro to secure my Spring MVC webapp. I'm using Hibernate for persistence and so I have a HibernateRealm to get and populate an AuthenticationInfo object.
#Override
#Transactional
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
Account account = accountDao.findByUsername((String)token.getPrincipal());
SimplePrincipalCollection principals = new SimplePrincipalCollection(account, getName());
SimpleAccount info = new SimpleAccount(principals, account.getPassword());
return info;
}
Account is my custom user class. I use the DAO to retrieve an Account by username. I was wondering if there is any point in making this method #Transactional. This is a read only operation after all.
I'm also having the following problem: the DAO does sessionFactory.getCurrentSession() to get a session, but I'm getting a
HibernateException: No Session found for current thread
when the method gets called. I have these in my application context:
<tx:annotation-driven transaction-manager = "transactionManager" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I can't understand why Spring isn't opening a session for me.
Edit: To login, we do this in a Spring #Controller method using Shiro's Subject
#RequestMapping(value = "/account/login", method = RequestMethod.POST)
public String login(#RequestParam("username") String username, #RequestParam("password") String password) {
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
currentUser.login(token);
return "profile";
}
return "home";
}
Internally, Shiro uses the realm method I have above to get the stored username/password information. It uses an #Autowired DAO to check my database for the right account. It then matches the passwords with a CredentialsMatcher implementation.
So you have two problems. It is usually better to split such questions into two, since these problems are not really connected to each other.
No Session found for current thread
It seems that #Transactional annotations does not work. To be sure you may run you code or tests in Debug mode and look for the JdkDynamicAopProxy or something similar in the stack - if it is present, than your Realm is invoked through transactions-intercepting proxy, but I suppose that there is no proxy curently. For it to work you need to take from the SpringContext not the HibernateRealm directly but the interface that this realm is implementing. This is due to the fact that built-in standard java library proxies can deal only with interfaces.
As for making the read-only service methods transactional.
There are several valid reasons to do so:
Since you are using Hibernate it is really possible that you actually use more than one query to get your Account object. And if this account is modified concurrently it may lead to inconsistent state:
first query for Account retrieval
Account is modified or deleted
second query for Account retrieval - this query will see the results of modification that together with the results of the first query may lead to inconsistent behavior, but if first and second query were in the same transaction with the proper level of transaction isolation second query would not see the modifications.
Uniform access to the database - it is really helpful when all your database connectivity layer access the DB in one and the same way - I greatly simplifies maintaining and extending of the application.
Using some transactional hints like #Transactional(readOnly=true) may improve your performance with proper configuration (e.g. for the really high-loaded application readOnly queries may use secondary replica of the DB Server). It is really easier to setup the java.sql.Connection.setReadOnly() method as part of the Spring transactions, than in the other way.
It appears that Spring isn't creating a transactional proxy for your Realm bean. This is the only reason that I can see why a Hibernate Session isn't available - because the backing infrastructure isn't there (on the thread) ready for use.
As to your question, if you do want to mark it #Transactional, you might consider specifying #Transactional(readOnly=true)
Shiro creates it's own instance of my Realm and therefore Spring has no power over it to wrap it in a proxy. That's why it can't add the transactional behavior.

Categories

Resources