410 likes | 425 Views
Learn how to add persistence to a sample users app using Spring's ORM and data access strategies, including integration with Hibernate and JPA, and transaction management.
E N D
Высшая школа ИТИС Лекция 9 Spring Data Access, Transactions, AOP 28 октября 2013 Веб-разработка на Java Алина Витальевна Васильева доцент факультета Компьютерных Наук Латвийский Университет инженер-разработчик, Одноклассники, Mail.ru Group alina.vasiljeva@gmail.com
Exercise • Let’s add persistence to a sample users app, developed during Lecture 7 https://github.com/avasiljeva/spring-app.git • Atable displaying all existing users • Aform for adding new users to a system
ORM and Data Access in Spring • The Spring Framework supports integration with • Hibernate • Java Persistence API (JPA) • Java Data Objects (JDO) • iBATIS SQL Maps for resource management, data access object (DAO) implementations, and transaction strategies • Let’s examine integration with Java Persistence API
Spring Libraries • Maven dependencies: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>3.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.2.4.RELEASE</version> </dependency>
Hibernate Libraries <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.2.5.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.2.5.Final</version> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <version>1.0.0.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency>
JPA in Spring • To be able to use JPA it is necessary to obtain EntityManagerFactoryinstance • Spring JPA offers three ways of setting up the JPA EntityManagerFactorythat will be used by the application to obtain an entity manager • LocalEntityManagerFactoryBean • JNDI • LocalContainerEntityManagerFactoryBean
LocalEntityManagerFactoryBean • For simple deployment environments only, such as stand-alone applications and integration tests <beans> <bean id="myEmf" class="org.springframework.orm .jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="myPersistenceUnit"/> </bean> </beans> • The most limited option: • cannot refer to an existing JDBC DataSource • no support for global transactions
JNDI • Option for deploying to a Java EE server • Java EE server auto-detects persistence units (persistence.xml) and persistence-unit-ref entries in the Java EE deployment descriptor (web.xml), and defines environment naming context locations for those persistence units <beans> <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/> </beans>
LocalContainerEntityManagerFactoryBean • Option for full JPA capabilities in a Spring-based application environment • We will examine this option in detail • Three steps are necessary: • Configure Entity Manager Factory in Spring context • Make your domain classes persistent entities • Create a DAO (Data Access Object)
Spring Context Configuration • The following has to be configured in Spring context: • "tx" XML namespace and schema • PersistenceAnnotationBeanPostProcessor • Entity Manager Factory • Transaction Manager
"tx" XML namespace and schema <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!– bean definitions here --> </beans>
PersistenceAnnotationBeanPostProcessor BeanPostProcessor that processes @PersistenceUnitand @PersistenceContextannotations, for injection of the corresponding JPA resources @EntityManagerFactoryand @EntityManager <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> You can either register it explicitly: or register all standard post-processor by adding: <!-- post-processors for all standard config annotations --> <context:annotation-config/>
Registering Entity Manager Factory <bean id="entityManagerFactory" class="org.springframework.orm.jpa .LocalContainerEntityManagerFactoryBean"> <!– name of a unit in persistence.xml --> <property name="persistenceUnitName" value="hibernate_mysql"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa .vendor.HibernateJpaVendorAdapter" /> </property> </bean> Don’t forget a file: src\main\resources\META-INF\persistence.xml
persistence.xml <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name="hibernate_mysql" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.username" value="root"/> <property name="hibernate.connection.password" value="root"/> <property name="hibernate.connection.url" value="jdbc:mysql://localhost/users-app"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence>
Configuring a Transaction Manager <bean id="txManager" class="org.springframework.orm.jpa .JpaTransactionManager"/> <tx:annotation-driven transaction-manager="txManager"/>
Configuring a Transaction Manager <bean id="txManager" class="org.springframework.orm.jpa .JpaTransactionManager"/> <tx:annotation-driven transaction-manager="txManager"/> Configuration is Completed !!!
Persistent Entity @Entity public class User { @Id @GeneratedValue private long id; private String firstName; private String lastName; . . . }
DAO @Component("userService") @Transactional public class UserServiceImpl implements UserService { @PersistenceContext private EntityManager em; public List<User> getUsers() { return (List<User>)em.createQuery( "select u FROM User u").getResultList(); } public void addUser(User user) { em.persist(user); } }
@Transactional • We added @Transactionalannotation to UserServiceImpl • It is a way to configure transactional behavior declaratively • In this case Spring AOP mechanism is assuring the transaction management • It is also possible to manage transactions programmatically
Programmatic Transaction Management public class UserServiceImpl implements UserService { @PersistenceUnit private EntityManagerFactory emf; public void addUser(User user) { EntityManager em = this.emf.createEntityManager(); try { em.getTransaction().begin(); em.persist(user); em.getTransaction().commit(); } finally { if (em != null) { em.close(); } } } }
Spring AOP • AOP = Aspect Oriented Programming • Complements OOP by providing another way of thinking about program structure • Aspects enable the modularization of crosscutting concerns such as • Transaction management • Logging • Caching • Authentication . . .
AOP Concepts • Aspect: a modularization of a concern that cuts across multiple classes • Join point: a point during the execution of a program (execution of a method, handling of an exception) • Advice: action taken by an aspect at a particular join point • "around“, "before“, "after“, …
AOP Concepts • Pointcut: a predicate that matches join points • Advice is associated with a pointcut expression and runs at any join point matched by the pointcut • Target object: object being advised by one or more aspects • AOP proxy: an object created by the AOP framework in order to implement the aspect contracts
AOP Exercise Let’s add a logging aspect to service layer in our Users App • Add AOP dependencies to Maven configuration • Create an Aspect • Configure the Aspect in Spring context
AOP Libraries <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.3</version> </dependency>
Creating an Aspect Aspect and “after” advice: @Aspect public class LoggerAspect { @After("within(ru.kfu.itis.service..*)") public void log(JoinPoint point) { /* simply log the fact of execution into server console */ System.out.println("LoggerAspect: " + point.toLongString()); } }
Configuring an Aspect <beans ... xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" ... http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="loggerAspect" class="ru.kfu.itis.aspect.LoggerAspect"/> <aop:aspectj-autoproxy> <aop:include name="loggerAspect"/> </aop:aspectj-autoproxy> </beans>
Server Console [INFO] Started Jetty Server [INFO] Starting scanner at interval of 10 seconds. LoggerAspect: execution(public abstract java.util.List ru.kfu.itis.interfaces.UserService.getUsers()) LoggerAspect: execution(public abstract void ru.kfu.itis.interfaces.UserService.addUser(ru.kfu.itis.domain.User)) LoggerAspect: execution(public abstract java.util.List ru.kfu.itis.interfaces.UserService.getUsers()) Now every invocation of UserService method is logged into server console
Types of advice • @Before: executes before a join point • @AfterReturning: to be executed after a join point completes normally • @AfterThrowing: to be executed if a method exits by throwing an exception • @After(finally): to be executed regardless of the means by which a join point exits (normal or exceptional) • @Around: surrounds a join point such as a method invocation • the most powerful kind of advice
Examples of Pointcuts • Any public method • Any method with a name beginning with "set“ • Any join point within the package or sub-package execution(public * *(..)) execution(* set*(..)) within(com.xyz.service..*)
Examples of Pointcuts • Any join point which takes a single parameter, and where the argument passed at runtime is Serializable • Any join point on Spring beans having names that match the wildcard expression '*Service' args(java.io.Serializable) bean(*Service)
Examples of Pointcuts • Any join point which takes a single parameter, and where the argument passed at runtime is Serializable • Any join point on Spring beans having names that match the wildcard expression '*Service' args(java.io.Serializable) bean(*Service)
AOP Proxies • Spring AOP defaults to using standard JDK dynamic proxies • is used if the target object to be proxied implements at least one interface • CGLIB proxy is used if a business object does not implement an interface
Understanding Proxies public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new SimplePojo()); factory.addInterface(Pojo.class); factory.addAdvice(new RetryAdvice()); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); } }
Back to Transactions @Transactional invokes Spring AOP mechanism @Component("userService") @Transactional public class UserServiceImpl implements UserService { @PersistenceContext private EntityManager em; public List<User> getUsers() { return (List<User>)em.createQuery( "select u FROM User u").getResultList(); } public void addUser(User user) { em.persist(user); } }
More About @Transactional • @Transactional is metadata that specifies that an interface, class, or method must have transactional semantics • Example: “start a brand new read-only transaction when this method is invoked, suspending any existing transaction” • Configurable properties: • proparation, isolation, read-only, timeout, rollback options
More About @Transactional • @Transactional default properties • Propagation = Propagation.REQUIRED • Isolation level = Isolation.DEFAULT • Transaction is read/write • Timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported • Any RuntimeException triggers rollback, and any checked Exception does not
Customization @Transactional( readOnly=true, propagation = Propagation.REQUIRED, isolation = Isolation.READ_UNCOMMITTED, timeout = 1) public List<User> getUsers() { return (List<User>)em.createQuery( "select u FROM User u").getResultList(); }
Resources ORM Data Access JPA in Spring http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/orm.html#orm-jpa Aspect Oriented Programming with Spring http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/aop.html Transaction Management in Spring http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/transaction.html