Hooking into start of JDBC transaction - java

I have a Spring Boot webapp connected to a Postgres 9.6 database.
I use Spring's JdbcTemplate for executing SQL statements.
Each table in my database has triggers for INSERT, CREATE and DELETE statments. These triggers copy the affected rows into a history table.
I want the triggers to also save the application user ID of the user who made the change.
According to https://stackoverflow.com/a/13172964/2591231 I can achieve my goal by having the application insert the current user id into a temporary table at the start of each transaction and having the triggers read from the temporary table.
A similar method, mentioned in several other places, is executing:
SET LOCAL application_name = "my_application_user", then reading application_name inside the triggers. Again, this has to be done at the start of each transaction.
I'm looking for way, which is orthogonal to business code (I don't want each DAO to explicitly set user ID), to hook into the start of each transaction in order to run a specific SQL statement before any other statement in the same transaction.
I need this to work for both implicit transactions (single invocations of JdbcTemplate) and transactions demarcated declaratively with Spring's #Transactional annotation.

First of all, JdbcTemplate does not provide transaction support out-of-the-box (see here). So, in order to intercept all #Transaction annotated code AND every call to JdbcTemplate, this could be done at DataSource level, as commented earlier by Serge Bogatyrev.
I have a Spring Web project where I tested this approach. I defined a replacement DataSource #Bean called MyDataSource that extends BasicDataSource, and overwrites its getConnection() method so that it creates the temp table and insert the user_id before returning the connection.
It worked for #Transaction calls and pure JdbcTemplate calls.
If you want to strictly tie this temp table update at the start of each transaction, do this same strategy for defining the PlatformTransactionManager #Bean. You only need to overwrite the doBegin() method. And don't forget to annotate with #Transaction all methods calling JdbcTemplate.
PS1: Make sure to call DROP TABLE IF EXISTS temp_table_name prior creating the temp table, in order to replace the DISCARD ALL on connection returning to pool, as mentioned here.
PS2: This whole solution of creating a temp table doesn't smell well. I wouldn't implement it myself. I would prefer to take a deep breath and add created_by and updated_by columns to all my tables.

You can take advantage of Spring AOP for setting up the user. The aspect will make a call to the database to set up the application user.
In my example, a stored procedure is used to set up the application user responsible for creating, modifying, or deleting a record . You can customize it according to your requirements. Here is the example aspect which retrieves the user from the HTTP request and then makes a call to the stored procedure,
#Component
#Aspect
#Slf4j
public class SetUserAspect {
private final JdbcTemplate jdbcTemplate;
#Autowired
public SetUserAspect(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Before("execution(* com.basaki.example.service.BookService.*(..) )")
public void setUser(JoinPoint jp) {
log.info("In class: " + jp.getSignature().getDeclaringTypeName() +
" - before method: " + jp.getSignature().getName());
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if (request != null) {
String user = request.getHeader("user");
if (user != null) {
log.info("Setting user " + user);
SimpleJdbcCall
jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("example_book_schema")
.withFunctionName("set_user");
SqlParameterSource
in =
new MapSqlParameterSource().addValue("audit_user",
user);
jdbcCall.executeFunction(String.class, in);
}
}
}
}
All the CRUD operations are performed in BookService, essentially a DAO.
Here is the stored procedure used for setting up the user,
CREATE OR REPLACE FUNCTION example_book_schema.set_user(
audit_user TEXT
) RETURNS BOOLEAN STABLE LANGUAGE SQL AS $$
SELECT set_config('book.audit_user', audit_user, true);
SELECT TRUE;
Restricting Pointcuts to Only Transactional Methods
You can restrict the points cuts to only transactional methods in BookService by adding an additional clause in the Before advice.
#Before("execution(* com.basaki.example.service.BookService.*(..) ) " +
"&& #annotation(annotation)")
public void setUser(final JoinPoint jp, final Transactional annotation) {
...
}

You can use #EntityListeners to listen change of entity in application context, then collect whatever information (entity value, authentication user, etc...) and then insert to your history table. Example you can follow here: http://www.baeldung.com/database-auditing-jpa

You can create view, add user id column, and use your triggers to deal with updates. So that is yet another way to code it at DB side. That way you are supposed to pass it every time, so no other changes are needed.
Going to Java/Spring side.
A bit oldish style: TransactionTemplate - that way you have full control, but your dao needs more code, since transaction management needs to be done there.
Other option is to create proxy on
org.springframework.jdbc.datasource.DataSourceTransactionManager and do your job at doBegin, then your proxy needs to be passed to transaction manager. And that is the way to go for me.

Related

spring async method call with JPA transactions

I am implementing a backend service with Spring Boot. This service receives a REST request and executes some database operations and finally updates the status of the record.
After that, I would like to start a new async process and execute another data manipulation on the same record this way:
#Service
public class ClassA {
#Autowired
private ClassB classB;
#Autowired
private MyEntityRepository repo;
#Transactional
public void doSomething(Long id) {
// executing the business logic
if (isOk()) {
repo.updateStatus(id, Status.VERIFIED)
}
// I need to commit this DB transaction and return.
// But after this transaction is committed, I need
// to start an async process that must work on the
// same record that was updated before.
classB.complete(id);
}
}
And this is my async method:
#Service
public class ClassB {
#Autowired
private MyEntityRepository repo;
#Async
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void complete(Long id) {
Optional<MyEntity> myEntity = repo.findById(id);
if (myEntity.isPresent() && myEntity.get().getStatus == Status.VERIFIED) {
// execute 'business logic B'
}
}
}
The classA.doSomething() is called multiply times with the same id but business logic B must be executed only when the record status in the DB is VERIFIED.
The above solution works fine.
But my concern is the following: My test database is small and the classA.doSomething() method always finishes and closes its transaction BEFORE the classB.complete() starts to check the status of the same record in the DB. I see in the log that the SQLs are executed in the proper order:
* UPDATE STATUS FROM TABLE ... WHERE ID = 1 // doSomething()
* COMMIT
* SELECT * FROM TABLE WHERE ID = 1 // complete()
But is that 100% guaranteed that the 1st, classA.doSomething() method will always finish and commit the transaction before the 2nd classB.complete() async call check the status of the same record?
If the async method classB.complete() will be executed before classA.doSomething() finishes and execute its DB commit then I will break the business logic and the business logic B will be skipped (the new DB transaction will not see the updated status yet) and that will cause a big issue. Maybe this can happen if the database is huge and the commit takes longer than it takes in my small test DB.
Maybe I can operate with the DB transaction isolation levels described here but changing this can cause another issue in another part of the app.
What is the best way to implement this logic properly which guarantees the proper execution order with the async method?
It is NOT GUARANTEED that "the 1st, classA.doSomething() method will always finish and commit the transaction before the 2nd classB.complete() async call check the status of the same record".
Transactions are implemented as some kind of interceptors appropriate for the framework (this is true for CDI too). The method marked #Transactional is intercepted by the framework, so the transaction will not end before the closing } of the method. As a matter of fact, if the transaction was started by another method higher in the stack, it will end even later.
So, ClassB has plenty of time to run and see inconsistent state.
I would place the 1st part of doSomething in a separate REQUIRES_NEW transaction method (you may need to place it in a different class, depending on how you configured transaction interceptors; if you are using AOP, Spring may be able to intercept calls to methods of the same object, otherwise it relies on the injected proxy object to do the interception and calling a method through this will not activate the interceptor; again this is true for other frameworks as well, like CDI and EJB). The method doSomething calls the 1st part method, which finishes in a new transaction, then ClassB can continue asynchronously.
Now, in that case (as correctly pointed out in the comment), there is a chance that the 1st transaction succeeds and the 2nd fails. If this is the case, you will have to put logic in the system about how to compensate for this inconsistent state. Frameworks cannot deal with it because there is not one recipe, it is a per case "treatment". Some thoughts, in case they help: make sure that the state of the system after the 1st transaction clearly says that the second transaction should complete "shortly after". E.g. keep a "1st tx committed at" field; a scheduled task can check this timestamp and take action if it is too far in the past. JMS gives you all this - you get retries and a dead letter queue for the failed cases.

Keep logged user in memory from database - Spring Security

I'm writing web app with spring security, I have already default security implementation it works fine, but I have question about getting data from database. How can I keep information from database without executing query to database everytime?
Look at this. User is my entity class, but for me it is not effective to retrive data from database everytime. Everytime I refresh that /welcome it will execute query, I'm using Spring data jpa so it's fine but does not make sense since there is no change in database. So what I want to do is to keep user and retrive his data from db once in my app. Is there any way to do it?
#RequestMapping(value = "/userpanel")
public String userpanel(Model model, Principal principal){
String loggedUserName = principal.getName();
Optional<User> user = userService.findByUserName(loggedUserName);
if(user.isPresent()){
model.addAttribute("user", user.get());
}
return "userpanel";
}
That's exactly what a custom UserDetails object is for. Make your User entity implement UserDetails, and make your UserDetailsService return it.
This way, SecurityContextHolder.getContext().getAuthentication().getPrincipal() will return your entity. You will also be able to inject it using #AuthenticationPrincipal.
This of course assumes that you have not set session creation to stateless (i.e. your security provider is not re-authenticating the user for every request).
Just remember that it is bad practice to keep sensitive data in memory for a prolonged period of time.
Spring includes its own caching system: https://spring.io/guides/gs/caching/
To use it, annotate a method that may take while to fetch something from the database with #Cacheable(String)
Then, in the application class, you can add the #EnableCaching annotation.
Google Guava also has its own caching system.
Example usage is like so:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<String, User>() {
public Graph load(User user) throws AnyException {
return loadFromDatabase(user);
}
});
This will store a maximum of 1000 users in memory and deletes entries when it gets too big. It will also delete entries after 10 minutes.
It acts like a map, but calls the load(User) method if the key (user ID) is not present.

Quartz job integrated in Spring does not refresh Hibernate Session

I'm using Hibernate and Spring (in mode OpenSessionInView) in a webapp.
I've introduced Quartz for scheduling Job for checks.
To be able to inject beans into the Job I make use of the following approach: https://gist.github.com/jelies/5085593
A job in my app is like:
#Service
public class SampleJob implements Job {
#Autowired
private SampleBusiness sampleBusiness;
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
for(Student student : sampleBusiness.getStudents()) {
System.out.println(student.getName());
}
}
}
When the webapp starts, and at the first execution of the Job the result is:
Tom
Bruce
Steven
The result is ok.
After that I change in the webapp the name of "Tom" in "Tommy".
On the next execution of the Job the result is:
Tom
Bruce
Steven
Why Hibernate session holds the old data?
I think I have to attach to the Job an Interceptor like I do for my other beans, but I'm not able to attach it to the Job.
Thanks
The possible reason for the above mentioned situation might be the cache oh the hibernate.
Hibernate uses two levels of caching, level 1 caching and level 2 caching.
The level 1 caching caches the data that is read frequently, but in case data is not present in hibernate cache it then looks for the same in the level 2 cache.
If the data is present in the cache level2, then it returns the data, if the data is not present here also it then actually hits the database to retrieve the data.
You should try to clear the cache data before hitting the same method.
Although whenever you update a record from the cached data, this data is re-cached. but in case you are trying to update data from other thread or session, it might not get reflected in the first thread, as the data is already present in the level 1 cache of this session.
I hope this drives you to the right direction.

jBilling: How to create payment programmatically?

How can I create payment in jBilling programmatically from scheduled plugin? The problem is I want to create a payment which is not linked to any invoice, so I try to use
applyPayment(PaymentDTOEx payment, Integer invoiceId, Integer executorUserId)
with invoiceId=null, but it leads to an error:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Initially I try:
IPaymentSessionBean psb = Context.getBean(Context.Name.PAYMENT_SESSION);
psb.applyPayment(new PaymentDTOEx(paymentWS), null, userID);
Later I added "userbl.webServicesAuthenticate(user, pass)" before, but result is the same.
I think I missed something important and maybe doing it completely wrong.
I've never used jBilling before, but after a bit of googling it seems like it uses Spring to manage transactions etc.
From the error you've quoted it looks like you don't have a Hibernate session open. If you were using a web framework (like Spring MVC, for example), the Hibernate session lifecycle is usually managed for you transparently using a servlet filter.
If you're executing a payment from a scheduled service, then you may need to open and close the Hibernate session yourself in your service. There's some documentation here that describes how to do this programmatically using Spring.
Also take a look at the #Transactional annotation. It might be as simple as annotating your scheduled job method with this.
Thanks to rcgeorge23!
The problem was there were no active Hibernate session opened. Here is working code:
IPaymentSessionBean psb = Context.getBean(Context.Name.PAYMENT_SESSION);
//transaction manager is available in jBilling like that:
PlatformTransactionManager txManager = Context.getBean(Context.Name.TRANSACTION_MANAGER);
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("CreatePaymentTransaction");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); //not sure which strategy is best in this case
TransactionStatus status = txManager.getTransaction(def);
String ret;
try {
ret =Integer.toString(psb.applyPayment(new PaymentDTOEx(paymentWS), null, userID));
}
catch (Exception ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
You can also create plug-in for user.
for example when user will created or added then the default amount should be paid.
for that you have to write a plug-in and it will occure when user will created.

jpa merge unmanaged entity

I would like to make an unmanaged entity managed in another Persistence Context. I read that this can be made with merge:
em.merge(user);
But if I do this it is not added to the context:
boolean isManaged = em.contains(user);
is always false.
Even if I make the following:
User dbuser = em.find(User.class, user.getId());
em.merge(user);
boolean isManaged = em.contains(user);
The dbuser and user are exactly the same.
What am I doing wrong?
I am using JPA, MySql DB, JBoss EAP 6.1
Call entityManager.flush()to commit your merge action into the database.
Ususally the commit is delayed. For example if your method has an #TransactionAttribute annotation. The transaction will be commited after the method has finished. But if you call em.contains(user) without a commit you just get the old state.

Categories