Project

General

Profile

Sample-spring-hibernate » History » Revision 19

Revision 18 (Henning Blohm, 02.03.2020 21:46) → Revision 19/20 (Henning Blohm, 07.09.2021 19:04)

h1. A sample using Hibernate and Spring on Z2 

 This sample is very similar to [[Sample-hibernate-basic]] but differs in that we use the Spring framework throughout... 

 * for assembly within the modules and to wire services between modules 
 * for declarative transaction demarcation 
 * for JPA entity manager injection  

 This is another practical application of [[How to Spring]]. 

 This sample is stored in "z2-samples.spring-hibernate":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate. 

 Check out [[How to transaction management]] for more infos on transaction management on Z2. See related examples  

 * [[Sample-springds-hibernate]] for an even tighter Spring embrace,  
 * [[Sample-jta-spring]] for integration with a third-party transaction manager 
 * [[Sample-spring-basic]] for a starter on Spring integration with Z2 
 
 h2. Prerequisites 

 {{include(Java Version Requirements)}} 

 You need to run Java DB as network server on localhost. This is explained next. 

 The application will create a database "z2-samples" 

 {{include(How to run Java db)}} 


 h2. Run it 

 If you have the database, the fastest way to verify whether it runs is: 

 {{include(Install_sample_prefix)}} 

 Check out the sample 

 <pre><code class="bash"> 
 git clone -b v2.9 v2.8 https://www.z2-environment.net/git/z2-samples.spring-hibernate 
 </code></pre> 

 {{include(Install_sample_postfix)}} 

 When running, go to http://localhost:8080/spring-hibernate. You should see this: 

 !spring-hibernate.png! 

 h2. Details 

 Similar to [[Sample-hibernate-basic]], the assumption of this example is that of a re-use domain module *com.zfabrik.samples.spring-hibernate.domain* that implements a "Thingy Repository" and is used from a web application that is in another module *com.zfabrik.samples.spring-hibernate.web*. The domain module exposes the Thingy Repository as a Z2 component - from a Spring application context defined bean -    that is imported into the application context of the Web application and injected into the controller filter by Spring. The controller uses declarative transaction demarcation. 

 The domain module makes use of Hibernate's JPA implementation using Spring's entity manager injection and integrates with the transaction management provided by *com.zfabrik.jta*. 

 h3. The domain module and its persistence context 

 The domain module *com.zfabrik.samples.spring-hibernate.domain* defines a persistence unit "thingies" in "java/src.impl/META-INF/persistence.xml":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/java/src.impl/META-INF/persistence.xml, i.e. in its implementation. That makes sense, as the XML file will be looked up with a class loader and we do not intent to retrieve from another module. Or, put differently, the persistence unit is not part of the module's API. 

 In order to integrate with the built-in transaction management the <code>persistence.xml</code> declares the JTA data source  

 <pre><code class="xml"> 
 <jta-data-source>components:com.zfabrik.samples.spring-hibernate.domain/DB</jta-data-source> 
 </code></pre> 

 and the _Transaction Manager Lookup_  

 <pre><code class="xml"> 
 <property name="hibernate.transaction.manager_lookup_class" value="com.zfabrik.hibernate.TransactionManagerLookup" /> 
 </code></pre> 

 The former points to the data source component "com.zfabrik.samples.spring-hibernate.domain/DB":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/DB.properties, while the latter makes sure Hibernate can register with the transaction manager implementation.  

 The persistence unit defines only one entity. The Thingy as in "Thingy.java":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/java/src.api/com/zfabrik/samples/spring_hibernate/thingies/Thingy.java. That is an API-exposed type. We use the simplified pattern of exposing persistent objects in the API rather than using Data Transfer Objects (DTOs). 

 The application context of the module is defined in "java/src.impl/META-INF/applicationContext.xml":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/java/src.impl/META-INF/applicationContext.xml and looks like this: 

 <pre><code class="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:tx="http://www.springframework.org/schema/tx" 
	 xmlns:context="http://www.springframework.org/schema/context" 
	 xmlns:aop="http://www.springframework.org/schema/aop" 
	 xsi:schemaLocation=" 
	 http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
	 http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-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/aop    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 

	 <!-- annotation based config --> 
	 <context:component-scan base-package="com.zfabrik.samples.spring_hibernate" /> 
	 <context:annotation-config /> 

	 <!-- EntityManager injection --> 
	 <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> 

	 <!-- The actual EMF we use --> 
	 <bean id="entityManagerFactory" 	 class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
		 <property name="persistenceUnitName" value="thingies" /> 
	 </bean> 
    
 </beans> 
 </code></pre> 

 In short:  
 * We make sure we can use annotations 
 * We enable entity manager injection 
 * We initialize the entity manager factory from Spring 

 In the implementation class "ThingyRepositoryImpl":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/java/src.impl/com/zfabrik/samples/spring_hibernate/impl/thingies/ThingyRepositoryImpl.java we make use of these capabilities and declare a Spring bean "thingyRepository": 

 <pre><code class="java"> 
 @Repository("thingyRepository") 
 public class ThingyRepositoryImpl implements ThingyRepository { 
	 @PersistenceContext 
	 private EntityManager em; 

	 @Override 
	 public void store(Thingy thingy) { 
		 this.em.persist(thingy); 
	 } 

	 @SuppressWarnings("unchecked") 
	 @Override 
	 public Collection<Thingy> findAll() { 
		 return this.em.createQuery("select t from Thingy t").getResultList(); 
	 } 

	 @Override 
	 public void delete(int id) { 
		 Thingy t = this.em.find(Thingy.class, id); 
		 if (t != null) { 
			 this.em.remove(t); 
		 } 
	 } 
 } 
 </code></pre> 

 In order to expose that bean as a Z2 component for re-use from other modules, we declare a component "com.zfabrik.samples.spring-hibernate/repository":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/repository.properties: 

 <pre><code class="ruby">  
 com.zfabrik.component.type=org.springframework.bean 

 # 
 # Expose Spring defined data source 
 # 

 # 
 # the context that defines the bean (more than one 
 # bean can be exposed like this) 
 # 
 bean.context=com.zfabrik.samples.spring-hibernate.domain/applicationContext 

 # 
 # the bean name 
 # 
 bean.name=thingyRepository 
 </code></pre> 

 that is based on the application context component "com.zfabrik.samples.spring-hibernate/applicationContext":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.domain/applicationContext.properties: 

 <pre><code class="ruby">  
 com.zfabrik.component.type=org.springframework.context 

 # 
 # context config location is where the context is  
 # actually defined.  
 # 
 context.contextConfigLocation=classpath:META-INF/applicationContext.xml 
 </code></pre> 

 See also [[How to Spring]] for more details on these component types. 

 h3. The web module, transaction boundaries, and service re-use 

 Let's turn to the Web application in *com.zfabrik.samples.spring-hibernate.web/web*. This one is also Spring configured. In contrast to the service module, its application context is not initialized from a Z2 component but rather from the Web app context (see "web/WebContent/WEB-INF/web.xml":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.web/web/WebContent/WEB-INF/web.xml). It is stored in "web/WebContent/WEB-INF/applicationContext.xml":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.web/web/WebContent/WEB-INF/applicationContext.xml and looks like this: 

 <pre><code class="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:tx="http://www.springframework.org/schema/tx" 
	 xmlns:context="http://www.springframework.org/schema/context" 
	 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:security="http://www.springframework.org/schema/security" 
	 xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
	 http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-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/aop    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
	 http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.3.xsd"> 

	 <!-- Annotation Support --> 
	 <context:component-scan base-package="com.zfabrik.samples.spring_hibernate" /> 
	 <context:spring-configured /> 
	 <context:annotation-config /> 		

	 <!--   
	 This binds to java:comp/UserTransaction, which is ok in a Web app and considering 
	 that we configured Jetty JTA (see the transaction manager how-to in the Z2 Wiki and check environment/webServer/jetty.xml) 
     --> 
	
	 <tx:jta-transaction-manager/> 

     <!-- outside of a web app we would bind to com.zfabrik.jta transaction manager like this 

	 <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
	     <property name="userTransaction"> 
	         <bean class="com.zfabrik.tx.UserTransaction"/> 
	     </property> 
	 </bean> 
	  --> 

	 <!-- make sure we can use @Transactional with the Spring aspect --> 
	 <tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/> 

	 <!-- import external services --> 
     <bean id="thingyRepository" class="com.zfabrik.springframework.ComponentFactoryBean"> 
         <property name="componentName"    value="com.zfabrik.samples.spring-hibernate.domain/repository" /> 
         <property name="className"    value="com.zfabrik.samples.spring_hibernate.thingies.ThingyRepository" /> 
     </bean> 
	
 </beans> 

 </code></pre> 

 In short: 

 * We enable annotation based configuration 
 * We make the transaction manager available (for in-depth details see [[How to transaction management]]) 
 * We enabled annotation based transaction demarcation (i.e. the use of <code>@Transactional</code>) 
 * We import the Thingy Repository as a bean into this context. 

 The "ControllerFilter":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-hibernate/revisions/master/entry/com.zfabrik.samples.spring-hibernate.web/java/src.impl/com/zfabrik/samples/spring_hibernate/impl/web/ControllerFilter.java is configured by Spring, although it is instantiated by the Jetty Web container. That is happening because we use the Spring aspect (see [[How to Spring]] once more) and it is annotated with <code>@Configurable</code>. We let Spring inject the Thingy Repository and we mark the <code>doFilter</code> method as transactional. Here is its skeleton: 

 <pre><code class="java"> 
 @Configurable 
 public class ControllerFilter implements Filter { 

   @Autowired 
   private ThingyRepository thingyRepository; 
	
   @Transactional 
   public void doFilter(ServletRequest sreq, ServletResponse sres, 	 FilterChain chain) throws IOException, ServletException { 
    
     // do some work here 

   } 

   @Override 
   public void init(FilterConfig cfg) throws ServletException {} 
  
   @Override 
   public void destroy() {} 
 } 
 </code></pre> 

 h2. A final word 

 A lot of what happens here requires the right libraries to be available in the sample modules. These are provided via the references in the <code>z.properties</code> files in the respective Java modules.  
 In other words: There is some non-trivial mechanics going on here that - in the long run - you should be aware of and try to read carefully through [[How to Spring]]. As a result you get a lot of coding and modularization convenience in the combination of Spring and Z2.