Aspectran provides a powerful integration environment for processing complex database operations in an object-oriented manner by leveraging the Jakarta Persistence API (JPA). In particular, it automatically manages the lifecycle of the EntityManager through Aspect-Oriented Programming (AOP) and supports both declarative and explicit transaction management.
This guide explains the core components of JPA, such as the EntityManager, the type-safe query builder QueryDSL, the supported implementations (Hibernate and EclipseLink), and the required dependencies.
1. Understanding JPA and EntityManager
Overview of JPA (Jakarta Persistence API)
JPA is a standard specification for Object-Relational Mapping (ORM) technology that automatically connects Java objects with relational databases (RDB). It allows developers to manage data through Java classes (Entities) instead of writing complex SQL queries manually.
Core Role of the EntityManager
The EntityManager is the central interface for managing entity states and performing database operations in a JPA environment.
- Data Manipulation: Provides methods to save (
persist), update (merge), delete (remove), and retrieve (find) objects in the database. - Persistence Context: Acts as a cache between the application and the database. It tracks changes to entity objects within a transaction and synchronizes them with the database (Flush) at the most appropriate time to optimize performance.
Integration with Aspectran: Aspectran automates the lifecycle of the EntityManager and the start and completion of transactions through AOP. This allows developers to focus on implementing business logic without worrying about repetitive boilerplate code like connection management.
2. QueryDSL Integration and EntityQuery
While the standard JPA EntityManager provides powerful features, it has limitations when writing complex dynamic queries because it requires the use of string-based JPQL. Aspectran overcomes these limitations and enhances developer productivity by integrating QueryDSL at the engine level.
Benefits of Adopting QueryDSL
- Type-safety: Queries are written in Java code rather than strings, allowing syntax errors to be caught immediately at compile time.
- Ease of Dynamic Queries: Complex conditional query generation is handled intuitively through Java method calls.
- Increased Productivity: IDE autocomplete features can be used to write queries, reducing typos and speeding up development.
Aspectran’s EntityQuery Interface
Aspectran provides the EntityQuery interface, which combines the functionality of the EntityManager and QueryDSL’s JPQLQueryFactory. A single interface allows you to use both standard JPA methods and QueryDSL’s flexible API simultaneously.
Example Usage in a Data Access Object (DAO)
@Component
public class MemberDao {
private final EntityQuery entityQuery;
@Autowired
public MemberDao(EntityQuery entityQuery) {
this.entityQuery = entityQuery;
}
public Member findById(Long id) {
// Retrieve a single object using the standard JPA method
return entityQuery.find(Member.class, id);
}
public List<Member> findAllActive(String name) {
// Write a type-safe dynamic query using the QueryDSL API
QMember qMember = QMember.member;
return entityQuery.selectFrom(qMember)
.where(qMember.status.eq(Status.ACTIVE)
.and(qMember.name.contains(name)))
.fetch();
}
public void save(Member member) {
// Persist (save) the entity
entityQuery.persist(member);
}
}
3. Supported JPA Providers (Hibernate and EclipseLink)
JPA is a specification, and a provider (implementation) is required to actually run it. Aspectran officially supports the two most widely used providers in the industry.
- Hibernate: The most popular implementation, featuring powerful functionality and a vast ecosystem. This is the default recommended combination for Aspectran projects.
- EclipseLink: The JPA reference implementation, which strictly adheres to the standard specification. It is often used in environments where Oracle database integration or strict standards compliance is required.
Aspectran provides a consistent transaction management experience regardless of the provider used through abstracted configurations, allowing you to choose the best fit for your project.
4. How Transactions Work
JPA transactions in Aspectran are integrated into the lifecycle of a request processing unit called an Activity.
- Initialization (Before):
EntityManagerAdviceis called to set up the transaction configuration for the current context. It employs a Lazy Opening strategy, where the database connection is not established immediately but delayed until it is actually needed. - Logic Execution (Logic): When the first database operation occurs in the application, the
EntityManageris activated, and a physical transaction begins. - Successful Completion (After): If no exception occurs during logic execution, the transaction is committed using
commit(), and changes are permanently reflected in the database. - Exception Handling (Exception): If an exception occurs, the transaction is immediately rolled back via
rollback()to ensure data consistency. - Resource Cleanup (Finally): After the operation is complete, the
EntityManagerand database connection are safely released.
5. Dynamic Routing (Read-Write Splitting)
For high-availability environments where Primary (Write-only) and Replica (Read-only) databases are operated separately, Aspectran automatically routes transactions based on method name patterns.
@Component
@Bean(id = "entityQuery")
public class AppEntityQuery extends RoutingEntityQuery {
public AppEntityQuery() {
// (Primary Aspect ID, Replica Aspect ID)
super("primaryTxAspect", "replicaTxAspect");
}
}
- Routing Priority: If a write session is already open in the current execution context, it is reused even for read operations to ensure data consistency.
- Automatic Analysis: Methods starting with names like
find*,select*, andquery*are automatically routed to the read-only session.
6. Precise Control via the @Hint Annotation
In addition to automatic routing based on method names, you can use the @Hint annotation to directly define transaction attributes for specific methods.
@Service
public class MemberService {
private final EntityQuery entityQuery;
@Autowired
public MemberService(EntityQuery entityQuery) {
this.entityQuery = entityQuery;
}
@Hint(type = "transactional", value = "readOnly: true")
public List<Member> getMembers() {
// Forces routing to a read-only session.
// Also increases safety by throwing an exception if a data modification is attempted within this context.
return entityQuery.select(qMember).from(qMember).fetch();
}
}
7. Explicit Aspect Definition
If you need to control transaction behavior more precisely at the code level, you can directly define an Aspect class that inherits from EntityManagerAdvice. This allows you to configure transaction opening strategies (Lazy vs. Eager) to suit your specific needs.
@Component
@Bean(lazyDestroy = true)
@Aspect(id = "txAspect")
@Joinpoint(pointcut = "+: **@entityQuery")
public class ConsoleTxAspect extends EntityManagerAdvice {
@Autowired
public ConsoleTxAspect(@Qualifier("entityManagerFactory") EntityManagerFactory factory) {
super(factory);
}
@Before
public void before() {
// Required: Method must be defined to activate the transaction.
// Option 1: Lazy Opening (Leave body empty)
// Option 2: Eager Opening (Call super.open())
}
@After
public void commit() {
super.commit();
}
@ExceptionThrown
public void rollback() {
super.rollback();
}
@Finally
public void close() {
super.close();
}
}
8. Multi-EntityManagerFactory Environments
When connecting to different database systems simultaneously, you can explicitly specify which EntityManagerFactory each EntityQuery bean should use.
@Component
@Bean(id = "secondEntityQuery")
public class SecondEntityQuery extends DefaultEntityQuery {
public SecondEntityQuery() {
super("secondTxAspect");
// Explicitly set the bean ID of the EntityManagerFactory to be linked
setEntityManagerFactoryBeanId("secondEntityManagerFactory");
}
}
9. Dependencies
Key module information to include in your pom.xml to build an Aspectran JPA environment.
Core Integration Module
<dependency>
<groupId>com.aspectran</groupId>
<artifactId>aspectran-with-jpa</artifactId>
<version>${aspectran.version}</version>
</dependency>
JPA Specification and QueryDSL Dependencies
<!-- Jakarta Persistence API Standard Interface -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0</version>
</dependency>
<!-- QueryDSL JPA Support Library -->
<dependency>
<groupId>io.github.openfeign.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>7.1</version>
</dependency>
JPA Implementation Choice (Pick one)
Choose one implementation that fits your project environment.
<!-- Hibernate Core -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>7.3.0.Final</version>
</dependency>
<!-- OR EclipseLink -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>5.0.0-B13</version>
</dependency>
Appendix: Pointcut Patterns
Method for precisely filtering targets to which the transaction advice will be applied. Aspectran pointcuts use +: (include) and -: (exclude) prefixes.
- Targeting specific Bean ID:
+: **@entityQuery - Targeting specific Class/Interface:
+: **@class:com.example.repository.*Repository - Package scope:
+: **@com.example.service.**
Aspectran leverages an internal transaction stack to ensure data integrity and safe propagation even in nested call structures.