Spring – 33 – Aspect Oriented Programming (AOP) – 07 – JPA & Hibernate AspectJ Declarative Transaction Management

Merhaba Arkadaslar
Onceki bolumlerde Declarative Transaction yaklasimini @Transactional annotation ile kullandik.
Bu bolumde AOP yaklasimiyla , Declarative Transaction yonetiminini inceleyelim.

  • JPA/EclipseLink
  • Native Hibernate icin ornek yapalim.

Declarative Transaction yaklasimini su bolumde inceledik ;
Spring – 26 – Declarative Transaction JPA & Hibernate
Burada ilgili siniflari tekrar kullanacagiz , bu nedenle tum class’lari burada paylasmiyorum.

CustomerServiceImpl.java
CustomerServiceImpl sinifimizda @Transactional annotation kullanmamiza gerek yok.
Declarative Transaction yonetimini AOP yardimiyla gerceklestirecegiz.

package _38.aspectj.jpa.transaction.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import _38.aspectj.jpa.transaction.dao.AddressDAO;
import _38.aspectj.jpa.transaction.dao.CustomerDAO;
import _38.aspectj.jpa.transaction.model.Customer;

@Service
public class CustomerServiceImpl implements CustomerService{

	@Autowired
	private CustomerDAO customerDAO;

	@Autowired
	private AddressDAO addressDao;

	@Override
	public void insertCustomerData(Customer customer) {
		customerDAO.insertCustomer(customer);
		addressDao.insertAddress(customer.getAddress());
	}

	@Override
	public List listCustomers() {
		return customerDAO.findAllCustomers();
	}

}

38.aspectj.jpa.transaction.xml
XML configuration dosyamizi inceleyecek olursak entityManagerFactory ile ilgili ayarlardan onceki bolumlerden bahsetmistim.

tx:advice tag’inda transaction-manager attribute’e tanimladigimiz JpaTransactionManager bean id bilgisini veriyoruz.
list* ve get* metotlari veritabaninda ekleme guncelleme islemi olmayacagi icin read only olarak belirtiyoruz.

aop:config tag’imizda expression attribute’te service paketimizde yer alan siniflarin tamamini belirtiyoruz.
advice-ref olarak txAdvice’i veriyoruz.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:tx="http://www.springframework.org/schema/tx" 
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd ">

	<!-- scan packages -->
	
	<context:component-scan base-package="_38.aspectj.jpa.transaction.dao ,
					      _38.aspectj.jpa.transaction.service " />

	
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location">
			<value>jdbc/jdbc.properties</value>
		</property>
	</bean>

	<bean id="dataSourceId"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

		<property name="dataSource" ref="dataSourceId" />
		<property name="jpaVendorAdapter">
			<bean
				class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" />
		</property>
		<property name="packagesToScan" value="_38.aspectj.jpa.transaction.model" />

		<property name="jpaProperties">
			<props>
				<prop key="eclipselink.ddl-generation">drop-and-create-tables</prop>
				<prop key="eclipselink.logging.level">OFF</prop>
				<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
			</props>
		</property>

		<property name="jpaPropertyMap">
			<map>
				<entry key="eclipselink.weaving" value="false" />
			</map>
		</property>
	</bean>
 	

	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- all methods starting with 'list' or 'get' are read-only -->
			<tx:method name="list*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			 <!-- for other methods use the default transaction settings -->
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut id="serviceMethods" 
			expression="execution(* _38.aspectj.jpa.transaction.service.*.*(..))" />
		<aop:advisor pointcut-ref="serviceMethods" advice-ref="txAdvice" />
	</aop:config>
	
	<!-- TransactionManager -->
	
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	
	
</beans>

AspectJJPATransactionTest.java

package _38.aspectj.jpa.transaction.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import _38.aspectj.jpa.transaction.model.Address;
import _38.aspectj.jpa.transaction.model.Customer;
import _38.aspectj.jpa.transaction.service.CustomerService;

public class AspectJJPATransactionTest {

	public static void main(String[] args) {

		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("38.aspectj.jpa.transaction.xml");

		// get CustomerService bean
		CustomerService customerService = context.getBean(CustomerService.class);

		// prepare Customer and Address data.
		Customer customer = new Customer(1, "Levent", "Erguder");
		Address address = new Address(1, "Java Street", "34000", "Istanbul");
		customer.setAddress(address);

		//
		Customer customer2 = new Customer(2, "Orcun", "Erpis");
		Address address2 = new Address(2, "Bakanliklar Street", "06000", "Ankara");
		customer2.setAddress(address2);

		Customer customer3 = new Customer(3, "Hakan", "Gencel");
		// duplicate Address id , throw exception
		// customers3 record will be rollbacked too.
		Address address3 = new Address(2, "Alemdag Road", "34000", "Istanbul");
		customer3.setAddress(address3);

		customerService.insertCustomerData(customer);
		customerService.insertCustomerData(customer2);

		try {
			customerService.insertCustomerData(customer3);
		} catch (Exception e) {
			System.out.println("Rollback...");
			System.out.println(e.getMessage());
		}

		System.out.println("Customer List : ");
		for (Customer customerRecord : customerService.listCustomers()) {
			System.out.println(customerRecord);
		}

		context.close();

	}
}

Ornegimizi calistirdigimizda;

Rollback...
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '2' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO ADDRESS (ADDRESSID, CITY, STREET, ZIPCODE) VALUES (?, ?, ?, ?)
	bind => [4 parameters bound]
Query: InsertObjectQuery(Address [addressId=2, street=Alemdag Road, zipcode=34000, city=Istanbul])
Customer List : 
Customer [id=1, name=Levent, surname=Erguder, address=Address [addressId=1, street=Java Street, zipcode=34000, city=Istanbul]]
Customer [id=2, name=Orcun, surname=Erpis, address=Address [addressId=2, street=Bakanliklar Street, zipcode=06000, city=Ankara]]

39.aspectj.hibernate.transaction.xml
Ayni ornegimizi Hibernate icin yapalim.

transactionManager icin HibernateTransactionManager bean tanimini yapalim.
sessionFactory icin LocalSessionFactoryBean kullaniyoruz.

aop:config ve tx:advice taglari yine benzer sekilde olacak.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:tx="http://www.springframework.org/schema/tx" 
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd ">

	<!-- scan packages -->
	
	<context:component-scan base-package="_39.aspectj.hibernate.transaction.dao ,
					      _39.aspectj.hibernate.transaction.service " />

	
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location">
			<value>jdbc/jdbc.properties</value>
		</property>
	</bean>

	<bean id="dataSourceId"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">

		<property name="dataSource" ref="dataSourceId" />
		<property name="packagesToScan" value="_39.aspectj.hibernate.transaction.model" />

		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.show_sql">false</prop>
				<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
			</props>
		</property>

	</bean>
	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- all methods starting with 'list' or 'get' are read-only -->
			<tx:method name="list*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			 <!-- for other methods use the default transaction settings -->
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut id="serviceMethods" 
			expression="execution(* _39.aspectj.hibernate.transaction.service.*.*(..))" />
		<aop:advisor pointcut-ref="serviceMethods" advice-ref="txAdvice" />
	</aop:config>

	<bean id="transactionManager"
		class="org.springframework.orm.hibernate5.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	
</beans>

Ornegimizi calistirdigimizda ;

ERROR: HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
Rollback...
could not execute statement; SQL [n/a]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
Customer List : 
Nov 20, 2016 12:19:08 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Customer [id=1, name=Levent, surname=Erguder, address=Address [addressId=1, street=Java Street, zipcode=34000, city=Istanbul]]
Customer [id=2, name=Orcun, surname=Erpis, address=Address [addressId=2, street=Bakanliklar Street, zipcode=06000, city=Ankara]]

Github kaynak dosyalar/ source folder
leventerguder/injavawetrust-spring-tutorial

Yazimi burada sonlandiriyorum.
Herkese bol Javali gunler dilerim.
Be an oracle man , import java.*;
Levent Erguder
OCP, Java SE 6 Programmer
OCE, Java EE 6 Web Component Developer

Print Friendly, PDF & Email

Leave a Reply to Levent Erguder Cancel reply

Your email address will not be published. Required fields are marked *