Spring – 26 – Declarative Transaction JPA & Hibernate
Merhaba Arkadaslar.
Onceki bolumde Programmatic Transaction kavramini inceledik ve DataSourceTransactionManager i kullandik. Bu bolumde bir diger yaklasim olan Declarative Transaction ‘i inceleyecegiz , Native Hibernate ve JPA/EclipseLink entegrasyonu uzerinden orneklerimizi gerceklestirecegiz.
Spring , Declarative Transaction icin destek saglar.
Onceki bolumlerde Spring & JPA entegrasyonunu gerceklestirmistik.
Spring – 21 – JPA – 01
Spring – 22 – JPA – 02
Bununla birlikte JPA konusunda daha detayli bilgi icin ;
EclipseLink/Hibernate JPA
JPA/EclipseLink Example
Domain Layer / Model
Address.java
package _31.jpa.declarative.transaction.model; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Address { @Id private int addressId; private String street; private String zipcode; private String city; //getters and setters // constructors // toString ..... }
Customer.java
package _31.jpa.declarative.transaction.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class Customer { @Id private int id; private String name; private String surname; @OneToOne private Address address; //getters and setters // constructors // toString ..... }
DAO(Data Access Object) Design & Implementation
Bu kisimda basit olarak persist ve createQuery metodunu kullanacagiz.
AddressDAOImpl.java
@PersistenceContext annotationi EntityManager instance variable’i uzerine ekledik. Boylece EntityManager icin inject islemi gerceklestirilecektir.
persist metodu yardimiyla kayit islemini gerceklestiriyoruz. Dikkat edecek olursak Transaction begin ve commit metotlarini kullanmadik!
Zaten @PersistenceContext ile birlikte kullanmaya calisirsak hata aliriz ;
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager – use Spring transactions or EJB CMT instead
package _31.jpa.declarative.transaction.dao; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.stereotype.Repository; import _31.jpa.declarative.transaction.model.Address; @Repository public class AddressDAOImpl implements AddressDAO { @PersistenceContext private EntityManager entityManager; @Override public void insertAddress(Address address) { entityManager.persist(address); } }
CustomerDAOImpl.java
package _31.jpa.declarative.transaction.dao; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import org.springframework.stereotype.Repository; import _31.jpa.declarative.transaction.model.Customer; @Repository public class CustomerDAOImpl implements CustomerDAO { @PersistenceContext private EntityManager entityManager; private static final String ALL_CUSTOMERS = "Select c from Customer c"; @Override public void insertCustomer(Customer customer) { entityManager.persist(customer); } @Override public List<Customer> findAllCustomers() { TypedQuery<Customer> query = entityManager.createQuery(ALL_CUSTOMERS, Customer.class); return query.getResultList(); } }
Service
DAO Layer i kullanmak icin @Autowired annotation ile inject islemini gerceklestiriyoruz.
Burada dikkat edecegimiz nokta @Transactional annotation’idir.
insertCustomerData metodu icerisinde insertCustomer ve insertAddress metotlarini cagiriyoruz , Customer ve Address tablosuna kayit atiyoruz.
Burada @Transactional annotation’ini kullandigimiz icin bu metot boyunca transaction yonetimi gerceklesmektedir. Bu nedenle DAO Layer da begin commit metotlarini kullanmadik.
Transaction yonetiminin Service Layer’da olmasi daha uygun olacaktir.
CustomerServiceImpl.java
package _31.jpa.declarative.transaction.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import _31.jpa.declarative.transaction.dao.AddressDAO; import _31.jpa.declarative.transaction.dao.CustomerDAO; import _31.jpa.declarative.transaction.model.Customer; @Service public class CustomerServiceImpl implements CustomerService { @Autowired private CustomerDAO customerDAO; @Autowired private AddressDAO addressDao; @Transactional @Override public void insertCustomerData(Customer customer) { customerDAO.insertCustomer(customer); addressDao.insertAddress(customer.getAddress()); } @Transactional(readOnly = true) @Override public List<Customer> listCustomers() { return customerDAO.findAllCustomers(); } }
XML Configuration
31.jpa.declarative.transaction.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--Scan @Service , @Repository ... annotations --> <context:component-scan base-package="_31.jpa.declarative.transaction" /> <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="_31.jpa.declarative.transaction" /> <property name="jpaProperties"> <props> <prop key="eclipselink.ddl-generation">drop-and-create-tables</prop> <prop key="eclipselink.logging.level">OFF</prop> </props> </property> <property name="jpaPropertyMap"> <map> <entry key="eclipselink.weaving" value="false" /> </map> </property> </bean> <!-- Transactions --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> </beans>
EntityManagerFactory ve JDBC konfigurasyonlarini onceki bolumlerde inceledik.
xmlns:tx=”http://www.springframework.org/schema/tx” ve
xsi:schemaLocation olarak ilgili tanimlamalari eklememiz gereklidir !
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
Burada dikkat edecek olursak Transaction Manager olarak JpaTransactionManager i kullaniyoruz.
@Transactional annotation’ini kullanabilmek icin < tx : annotation> tanimlamasini kullaniyoruz.
<!-- Transactions --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
Run Application
Ornegimizi inceleyecek olursak ; 3 tane Customer ve Address objesi olusturup kaydetmeye calisiyoruz.
Burada dikkat edecegimiz nokta address3 icin id degerini 2 olarak verdik , id degeri unique olmasi gerektigi icin (primary key)
ORA-00001: unique constraint nedeniyle java.sql.SQLIntegrityConstraintViolationException a neden olacaktir.
Burada insertCustomerData metodu @Transactional olarak tanimlandigi icin Customer icin kaydin basarili olmasi yeterli olmayacaktir. Ilgili Address kaydi exceptiona neden olacaktir dolayisiyla roll-back olacagi icin Customer kaydi da eklenmeyecektir.
SpringJPADeclarativeTransactionTest.java
package _31.jpa.declarative.transaction.test; import org.springframework.context.support.ClassPathXmlApplicationContext; import _31.jpa.declarative.transaction.model.Address; import _31.jpa.declarative.transaction.model.Customer; import _31.jpa.declarative.transaction.service.CustomerService; import _31.jpa.declarative.transaction.service.CustomerServiceImpl; public class SpringJPADeclarativeTransactionTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "31.jpa.declarative.transaction.xml"); // get CustomerService bean CustomerService customerService = context.getBean(CustomerServiceImpl.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.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (LEVENT.SYS_C0022062) violated Error Code: 1 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]]
Output u inceleyecek olursak ;
customerService.insertCustomerData(customer3);
java.sql.SQLIntegrityConstraintViolationException a neden olacaktir. catch blogunda yakaladik ve e.getMessage() metodu calisti.
Sonrasinda Customer icin query calismaktadir ve ilk 2 kaydin basariyla eklendigini gorebiliriz.
Hibernate Example
Spring Hibernate entegrasyonunu onceki bolumlerde gerceklestirdik. Ilgili dependency tanimlini eklemistik.
Ilgili ornegi _32 paketinde bulabilirsiniz. Yukaridaki ornegimizden farkli olarak su kisimlarda degisiklikler olacak ;
CustomerDAOImpl.java
package _32.hibernate.declarative.transaction.dao; import java.util.List; import javax.persistence.TypedQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import _32.hibernate.declarative.transaction.model.Customer; @Repository public class CustomerDAOImpl implements CustomerDAO { private static final String ALL_CUSTOMERS = "Select c from Customer c"; @Autowired private SessionFactory sessionFactory; private Session getCurrentSession() { return sessionFactory.getCurrentSession(); } @Override public void insertCustomer(Customer customer) { Session session = getCurrentSession(); session.persist(customer); } @Override public List<Customer> findAllCustomers() { Session session = getCurrentSession(); TypedQuery<Customer> query = session.createQuery(ALL_CUSTOMERS, Customer.class); return query.getResultList(); } }
AddressDAOImpl.java
package _32.hibernate.declarative.transaction.dao; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import _32.hibernate.declarative.transaction.model.Address; @Repository public class AddressDAOImpl implements AddressDAO{ @Autowired private SessionFactory sessionFactory; private Session getCurrentSession() { return sessionFactory.getCurrentSession(); } @Override public void insertAddress(Address address) { Session session = getCurrentSession(); session.persist(address); } }
CustomerServiceImpl.java
package _32.hibernate.declarative.transaction.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import _32.hibernate.declarative.transaction.dao.AddressDAO; import _32.hibernate.declarative.transaction.dao.CustomerDAO; import _32.hibernate.declarative.transaction.model.Customer; @Service public class CustomerServiceImpl implements CustomerService{ @Autowired private CustomerDAO customerDAO; @Autowired private AddressDAO addressDao; @Transactional @Override public void insertCustomerData(Customer customer) { customerDAO.insertCustomer(customer); addressDao.insertAddress(customer.getAddress()); } @Transactional @Override public List<Customer> listCustomers() { return customerDAO.findAllCustomers(); } }
32.hibernate.declarative.transaction.xml
LocalSessionFactoryBean ve HibernateTransactionManager bean’lerini kullanalim.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!--Scan @Service , @Repository ... annotations --> <context:component-scan base-package="_32.hibernate.declarative.transaction" /> <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="_32.hibernate.declarative.transaction.model" /> <property name="hibernateProperties"> <props> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> </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 [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement Customer List : Nov 18, 2016 11:58:00 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
Leave a Reply