Proxy Pattern and Spring @ Transactional
What is a Proxy?
Proxy means ‘in place of’, representing or the authority to represent someone else, or a figure that can be used to represent the value of something. In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes.
We can add extra functionalities using the proxy object such as Caching, Transaction Management, or checking conditions after/before operations on the real object are invoked. In this article we will focus on Transaction Management via proxy in spring framework. But before that….
Let’s dig into the Proxy pattern
Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.
As shown in the above figure, the Client needs to perform operation() method on the RealSubject Object which is an implementation of Subject, but in reality it interact with it’s Proxy and calls the operation() on the proxy object which then internally calls the operation() of the RealSubject.
The Proxy intercepts all the calls to the RealSubject, with the RealSubject class being unaware of the proxy.
Transaction Management using @Transactional and Proxy Pattern
Let’s look into how plain JDBC Transaction Management works before jumping into Spring’s Transaction Management.
import java.sql.Connection;
Connection connection = dataSource.getConnection();
try (connection) {
connection.setAutoCommit(false);
// execute some SQL queries
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
- First we need to create a Connection object to start transaction, although in modern spring/spring-boot applications we will have data source configured for the same.
- Then we need to disable the auto-commit using connection.setAutoCommit(false);
This will allow us to group multiple subsequent queries under the same transaction, instead of committing every queries individually using (connection.setAutoCommit(true)). - If every thing went well then we will push all the changes at once using commit() method.
- OR rollback if we encounter any RuntimeException.
Spring’s Transaction Management
Now let’s have a look at what Spring transaction management usually looks like:
public class UserService {
@Transactional
public Long registerUser(User user) {
// execute some SQL
// userDao.save(user);
return id;
}
}
That’s All!, yes assuming that we have specified a transaction manager in the Spring Configuration.
Using the @Transactional on the UserService, the code above translates (simplified, actual implementation is illustrated in the next section) to this:
public class UserService {
public Long registerUser(User user) {
Connection connection = dataSource.getConnection();
try (connection) {
connection.setAutoCommit(false);
// execute some SQL that e.g.
// userDao.save(user);
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
}
}
This is all just standard opening and closing of a JDBC connection as seen above. That’s what Spring’s transactional annotation does for us automatically, without having to write it explicitly.
Where do proxies comes into the picture?
Spring creates proxies for classes that declare @Transactional
on the class itself or on members. The proxy is mostly invisible at runtime. It provides a way for Spring to inject behaviours before, after, or around method calls into the object being proxied.
Again taking the above example for reference,
class UserService {
@Transactional
public Long registerUser(User user) {
// execute some SQL
// userDao.save(user);
return id;
}
public void myOtherMethod() {
this.registerUser();
}
}
class UserServiceProxy extends UserService { // or implements MyInterface if proxyMode is not TARGET_CLASS and MyClass also implements MyInterface
UserService userService;
@Override
public Long registerUser() {
Connection connection = dataSource.getConnection();
try (connection) {
connection.setAutoCommit(false);
userService.myMethod();
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
}
@Override
public void myOtherMethod() {
userService.myOtherMethod();
}
}
In our application when we make request to registerUser(), we are actually interact with UserServiceProxy. As observed, through, the proxy mechanism only works when calls come in from some external object. When we make an internal call within the object, we are really making a call through the this
reference, and hence the proxy magic don’t comes to picture in this case.
I hope this article have helped you better understand Proxy pattern and one of it’s application in Spring i.e @ Transactional.