Screen Transition (With More than one Controllers)

multiple-relaiton-image-mvc2
In Spring MVC Web apps, there might be multiple combination between Controller, JSP(View), and Object class(Model). In other words, almost any combination is possible.

It is a lot more flexible compared to Struts which tied one Action class (controller class) tied with Form class (object model class).

mvc-classes-diagram-img1
There are number of Controller, Model classes, and views (JSP) in one Web App, and this examples shows the screen transition involved between two controllers and views (JSP).
screen-transition-img1 (1)
Summarized as four steps:

  1. First Controller calls the first JSP.
  2. First JSP calls the second Controller.
  3. Second Controller calls the second JSP.
  4. Second JSP is initialized.

Step 1 to 2. The first Controller (RmaController.java) calls main_01.jsp.
Step 2 to 3. The main_01.jsp calls second controller RmaDtlController.java.
Step 4. The second JSP (sub_line_01.jsp) is initialized.


Again (1 to 2), the first controller (RmaController.java) is invoked by a link, and calls second controller (RmaDtlController.java).
screen-tran-radio1
2015-06-07_22h29_16
Let’s look at step (2 to 3):
The main_01.jsp calls second controller RmaDtlController.java once the link is clicked (line 16).

<c:forEach items="${HDR_LIST}" var="rmaObj" varStatus="loop">
	<tr height="15">
		<td><form:radiobutton path="radioBtn" class='close clickIdx' data-id='${rmaObj.id}' /></td>
		<td><form:checkbox path="selectedCheckBox" value="${rmaObj.rmaNum}"/></td>
		<td id="id${loop.index}">${rmaObj.id}</td>
		<td id="idRmaNum${loop.index}">${rmaObj.rmaNum}</td>
		<td id="idStsCd${loop.index}">${rmaObj.rmaHdrStsCd}</td>
		<td id="idStsNm${loop.index}">${rmaObj.rmaHdrStsNm}</td>
		<td id="idTpCd${loop.index}">${rmaObj.rtrnTpCd}</td>
		<td id="idTpNm${loop.index}">${rmaObj.rtrnTpNm}</td>
		<td id="idRsnCd${loop.index}">${rmaObj.rtrnRsnCd}</td>
		<td id="idRsnNm${loop.index}">${rmaObj.rtrnRsnNm}</td>
		<td id="idCustCd${loop.index}">${rmaObj.sellToCustCd}</td>
		<td><a href="<c:url value='/edit/${rmaObj.id}' />" >Edit</a></td>
		<td><a href="<c:url value='/remove/${rmaObj.id}' />" >Delete</a></td>
		<td><a href="<c:url value='/cUrlValSubLine01Jsp/${rmaObj.rmaNum}' />" >Detail</a></td>
	</tr>
</c:forEach>

screen-tran-link-param-img1
In the line 16:
2.1. Link clicked contains an ID (RMA000016)
2.2.cUrlValSubLine01Jsp” specified in second JSP (sub_line_01.jsp) and it will be called
url-scrn-trans-rmaline1
2.3. The URL for second JSP: http://localhost:8080/NS2015V07/cUrlValSubLine01Jsp/RMA000016


Step 3: Second Controller calls the second JSP.

@RequestMapping("/cUrlValSubLine01Jsp/{rmaNum}")
public ModelAndView goToSubLine01Jsp(@PathVariable("rmaNum") String rmaNum,	Model model, HttpServletRequest req) {
	List<RmaLineModel> rmaLineModelList = RmaDtlBL.getRmaLineListWithCdTblNm(rmaNum, null);
	return getMVSubLine01(rmaNum, rmaLineModelList, req);
}

3.1. Returns ModelAndView object

private ModelAndView getMVSubLine01(String rmaNum, List<RmaLineModel> rmaLinsList, HttpServletRequest req) {

	RmaLineListModel newModel = new RmaLineListModel(rmaNum, rmaLinsList);

	ModelAndView modelAndView = new ModelAndView(VIEW.SUB_LINE_01.getVal());
	modelAndView.addObject(CONST.FORM_KEY.getVal(), newModel);
	// For item detail list
	modelAndView.addObject(CONST.LINE_LIST_MODEL.getVal(), newModel);
	// Setup Status drop down line the item list
	modelAndView.addObject(CONST.HDR_STS_LIST.getVal(), getList(CONST.HDR_STS_LIST.getVal()));
	// Item list for AJAX Auto complete
	req.getSession().setAttribute(CONST.MDSE_LIST.getVal(), getList(CONST.MDSE_LIST.getVal()));
	return modelAndView;
}

In the line 5:
3.2. Get the view name (“sub_line_01”) as constant (below) and set it to the object

enum VIEW {

	MAIN_01("main_01"),
	SUB_LINE_01("sub_line_01"),
	SUB_TP_01("sub_tp_01"),
	SUB_RSN_01("sub_rsn_01"),
	SUB_TP_RSN_01("sub_tp_rsn_01"),
	REDIRECT_HOME("redirect:/ns-home")
	;

	private String code;

	VIEW(String code) {
		this.code = code;
	}

	public String getVal() {
		return code;
	}
}

Step 4: Second JSP is initialized.
2015-05-29_07h12_12

Spring MVC Config

mvc-template1
This is my imagination of MVC model visualized:

    Model (Bean Object)
    View (JSP = magnifying glass)
    Controller (I personally don’t play a video game because it’s wasting time)

The term of “Spring MVC” is almost symbolized as J2EE Web Apps on Spring Framework. Today’s one of most popular J2EE framework. Compared to Struts, Spring is much easier for screen transition because it is not strongly tied with XML file such as struts-config.xml in Struts. Spring is a framework that try to decouple its between Model, View, and Controller.

There are three major XML files you will deal with:

spring-mvc-config-xmls1

web.xml

  • Context as root-context.xml (line 4)
  • Servlet as servlet-context.xml (line 18)
  • URL pattern (line 25)

servlet-context.xml

This is the file you have to deal with most often that defines all following info.

  • Defines Spring bean for Dependency Injection (DI) and Aspect Oriented (AOP)
  • DB config (Hibernate)

NOTE: There is other file root-context.xml. This can be empty for Spring MVC because it is used for non-web beans.


pom.xml

  • XML file for Apache Maven that defines all JAR files for this entire project

<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Processes application requests -->
<servlet>
	<servlet-name>appServlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
	
<servlet-mapping>
	<servlet-name>appServlet</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans 
    xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/mvc         http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop         http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context     http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/security     http://www.springframework.org/schema/security/spring-security-3.2.xsd
        http://www.springframework.org/schema/tx         http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
        >

    <!-- Enables the Spring MVC @Controller -->
    <annotation-driven />

    <context:component-scan base-package="com.ns.spring" />

    <!-- AOP -->
    <aop:aspectj-autoproxy>
        <aop:include name="aopBefore" />
        <aop:include name="aopAfter" />
        <aop:include name="aopAround" />
    </aop:aspectj-autoproxy>

    <!-- "id": Unique ID used by programs to identify the class. Unlike "name"  -->
    <!--       it cannot be more than one reference from the same Java object   -->
    <!-- "prototype": New instance each time when called                        -->
    <!-- "singleton": Single instance per Spring IoC container                  -->
    <beans:bean id="aopBefore"  class="com.ns.spring.aop.advise.AspectBefore" scope="prototype"/>
    <beans:bean id="aopAfter"   class="com.ns.spring.aop.advise.AspectAfter"  scope="prototype"/>
    <beans:bean id="aopAround"  class="com.ns.spring.aop.advise.AspectAround" scope="prototype"/>
    
    <!-- Configure Aspect Beans, without this Aspects advices wont execute -->
    <beans:bean name="aopAnnotationAspect" class="com.ns.spring.aop.AopAnnotationAspect" />
    <beans:bean name="aopByXmlConfig"  class="com.ns.spring.aop.advise.AopByXmlConfig" />
    <!-- Configure Beans for AOP -->
    <beans:bean name="rmaController"  class="com.ns.spring.RmaController" />
        
    <beans:bean name="aopTest" class="com.ns.spring.aop.test.AopTest">
        <beans:property name="name" value="Test123"></beans:property>
    </beans:bean>

    <!-- Configure EmployeeService bean -->
    <!-- "ref": Referencing a different bean (could be in a different XML file)    -->
    <beans:bean name="aopTestService" class="com.ns.spring.aop.test.AopTestService" scope="prototype">
        <beans:property name="aopTest" ref="aopTest"></beans:property>
    </beans:bean>

    <!-- AOP Config by XML-->
    <aop:config>
        <aop:aspect ref="aopByXmlConfig" id="aspectXMLConfigID" order="1">
            <aop:pointcut expression="execution(* com.ns.spring.aop.test.AopTest.getName())" id="getNamePointcut"/>
            <aop:around method="aopAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
        </aop:aspect>
    </aop:config>

    <tx:annotation-driven transaction-manager="transactionManager"/>

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

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources 
        in the /WEB-INF/views directory -->
    <beans:bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <beans:bean id="dataSource" 
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <beans:property name="url"      value="jdbc:mysql://localhost:3306/ns201501" />
        <beans:property name="username" value="root" />
        <beans:property name="password" value="nobu" />
    </beans:bean>

    <!-- Hibernate 4 SessionFactory Bean definition -->
    <beans:bean id="hibernate4AnnotatedSessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <beans:property name="dataSource" ref="dataSource" />
        <beans:property name="annotatedClasses">
            <beans:list>
                <beans:value>com.ns.spring.model.RMA_HDR</beans:value>
                <beans:value>com.ns.spring.model.RMA_LINE</beans:value>
                <beans:value>com.ns.spring.model.RTRN_TP</beans:value>
                <beans:value>com.ns.spring.model.RTRN_RSN</beans:value>
                <beans:value>com.ns.spring.model.RTRN_TP_RSN</beans:value>
                <beans:value>com.ns.spring.model.RTRN_TP_RSN_CMBN</beans:value>
                <beans:value>com.ns.spring.model.RMA_HDR_STS</beans:value>
                <beans:value>com.ns.spring.model.MDSE</beans:value>
                <beans:value>com.ns.spring.model.RmaLinePk</beans:value>
                <beans:value>com.ns.spring.model.RtrnTpRsnPk</beans:value>                                
                <beans:value>com.ns.spring.model.ui.RmaHdrModel</beans:value>
                <beans:value>com.ns.spring.model.ui.RmaHdrStsModel</beans:value>
                <beans:value>com.ns.spring.model.ui.RmaLineModel</beans:value>
                <beans:value>com.ns.spring.model.ui.RtrnRsnModel</beans:value>
                <beans:value>com.ns.spring.model.ui.RtrnTpModel</beans:value>
            </beans:list>
        </beans:property>
        <beans:property name="hibernateProperties">
            <beans:props>
                <beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</beans:prop>
                <beans:prop key="hibernate.show_sql">true</beans:prop>
            </beans:props>
        </beans:property>
    </beans:bean>
</beans:beans>    
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.ns.spring</groupId>
   <artifactId>NS2015V07</artifactId>
   <name>NS2015V07</name>
   <packaging>war</packaging>
   <version>1.0.0-BUILD-SNAPSHOT</version>
   <properties>
      <java-version>1.7</java-version>
      <org.springframework-version>4.0.3.RELEASE</org.springframework-version>
      <org.aspectj-version>1.7.4</org.aspectj-version>
      <org.slf4j-version>1.7.5</org.slf4j-version>
      <hibernate.version>4.3.5.Final</hibernate.version>
      <jackson.databind-version>2.2.3</jackson.databind-version>
   </properties>
   <dependencies>
      <!-- Spring -->
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>${org.springframework-version}</version>
         <exclusions>
            <!-- Exclude Commons Logging in favor of SLF4j -->
            <exclusion>
               <groupId>commons-logging</groupId>
               <artifactId>commons-logging</artifactId>
            </exclusion>
         </exclusions>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>${org.springframework-version}</version>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-tx</artifactId>
         <version>${org.springframework-version}</version>
      </dependency>
      <!-- Hibernate -->
      <dependency>
         <groupId>org.hibernate</groupId>
         <artifactId>hibernate-core</artifactId>
         <version>${hibernate.version}</version>
      </dependency>
      <dependency>
         <groupId>org.hibernate</groupId>
         <artifactId>hibernate-entitymanager</artifactId>
         <version>${hibernate.version}</version>
      </dependency>
      <!-- Apache Commons DBCP -->
      <dependency>
         <groupId>commons-dbcp</groupId>
         <artifactId>commons-dbcp</artifactId>
         <version>1.4</version>
      </dependency>
      <!-- MySQL -->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.9</version>
      </dependency>
      <!-- Spring ORM -->
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-orm</artifactId>
         <version>${org.springframework-version}</version>
      </dependency>
      <!-- AspectJ -->
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjrt</artifactId>
         <version>${org.aspectj-version}</version>
         <scope>runtime</scope>
      </dependency>
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjtools</artifactId>
         <version>${aspectj.version}</version>
      </dependency>
      <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.8.5</version>
      </dependency>
      <dependency>
         <groupId>cglib</groupId>
         <artifactId>cglib</artifactId>
         <version>2.2.2</version>
      </dependency>
      <!-- Logging -->
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>${org.slf4j-version}</version>
      </dependency>
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>jcl-over-slf4j</artifactId>
         <version>${org.slf4j-version}</version>
         <scope>runtime</scope>
      </dependency>
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-log4j12</artifactId>
         <version>${org.slf4j-version}</version>
         <scope>runtime</scope>
      </dependency>
      <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.15</version>
         <exclusions>
            <exclusion>
               <groupId>javax.mail</groupId>
               <artifactId>mail</artifactId>
            </exclusion>
            <exclusion>
               <groupId>javax.jms</groupId>
               <artifactId>jms</artifactId>
            </exclusion>
            <exclusion>
               <groupId>com.sun.jdmk</groupId>
               <artifactId>jmxtools</artifactId>
            </exclusion>
            <exclusion>
               <groupId>com.sun.jmx</groupId>
               <artifactId>jmxri</artifactId>
            </exclusion>
         </exclusions>
         <scope>runtime</scope>
      </dependency>
      <!-- @Inject -->
      <dependency>
         <groupId>javax.inject</groupId>
         <artifactId>javax.inject</artifactId>
         <version>1</version>
      </dependency>
      <!-- Servlet -->
      <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>servlet-api</artifactId>
         <version>2.5</version>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>javax.servlet.jsp</groupId>
         <artifactId>jsp-api</artifactId>
         <version>2.1</version>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>jstl</artifactId>
         <version>1.2</version>
      </dependency>
      <!-- Test -->
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.7</version>
         <scope>test</scope>
      </dependency>
      <!-- JSON -->
      <dependency>
         <groupId>com.googlecode.json-simple</groupId>
         <artifactId>json-simple</artifactId>
         <version>1.1.1</version>
      </dependency>
      <dependency>
         <groupId>com.google.code.gson</groupId>
         <artifactId>gson</artifactId>
         <version>2.3.1</version>
      </dependency>
      <!-- Jersey -->
      <dependency>
         <groupId>asm</groupId>
         <artifactId>asm</artifactId>
         <version>3.3.1</version>
      </dependency>
      <dependency>
         <groupId>com.sun.jersey</groupId>
         <artifactId>jersey-bundle</artifactId>
         <version>1.18.1</version>
      </dependency>
      <dependency>
         <groupId>org.json</groupId>
         <artifactId>json</artifactId>
         <version>20140107</version>
      </dependency>
      <dependency>
         <groupId>com.sun.jersey</groupId>
         <artifactId>jersey-core</artifactId>
         <version>1.18.1</version>
      </dependency>
      <!-- Jackson -->
      <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>${jackson.databind-version}</version>
      </dependency>
      <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.5.0</version>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <artifactId>maven-eclipse-plugin</artifactId>
            <version>2.9</version>
            <configuration>
               <additionalProjectnatures>
                  <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
               </additionalProjectnatures>
               <additionalBuildcommands>
                  <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
               </additionalBuildcommands>
               <downloadSources>true</downloadSources>
               <downloadJavadocs>true</downloadJavadocs>
            </configuration>
         </plugin>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.5.1</version>
            <configuration>
               <source>1.7</source>
               <target>1.7</target>
               <compilerArgument>-Xlint:all</compilerArgument>
               <showWarnings>true</showWarnings>
               <showDeprecation>true</showDeprecation>
            </configuration>
         </plugin>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.2.1</version>
            <configuration>
               <mainClass>org.test.int1.Main</mainClass>
            </configuration>
         </plugin>
      </plugins>
      <finalName>${project.artifactId}</finalName>
   </build>
</project>

Around Advise

aop-around-advice-header-img1

(1) Overview & (2) Configure
(3) Example to define Point Cut

In APO, Around Advice is the most powerful, and (believed to be) commonly used. It can be BEFORE or AFTER or even BOTH at the same time. To implements Around Advise, AspectJ provides @Around annotation. In order to use it, you need to define a something called “Point Cut“.

In the line 24-25:
aop-around-pointcut-arg1
3.1. This point cut is defined as Any method whose parameter is String

In the line 36-37:
aop-around-pointcut-within1
3.2. This point cut is defined as Any class under the package: com.ns.spring.dao.template

In the line 48-49:
aop-around-pointcut-exec1
3.3. This point cut is defined as Any method starts with “get” under this package: com.ns.spring.dao.templatepl class

In the line 54-55:
aop-around-pointcut-exec2
3.4. This point cut is defined as Any method in GenHbDaoImpl class


(4) Example to use Point Cut as Around Advice

In the line 57:
aop-around-advice-or-img1
4.1. The @Around advice executed if either condition of Point cut is met

In the line 62:
aop-around-advice-and-img1
4.2. The @Around advice executed if both conditions of Point cut are met


(5) What happen when executed

This example calls method “aroundAdvice” and:

  • Any process BEFORE line 73 (line 68 to 71), runs before the method call
  • Any process AFTER line 73 (line 77 to 80), runs after the method call
package com.ns.spring.aop.advise;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class AspectAround {

	private static final Logger log = LoggerFactory.getLogger(AspectAround.class);

	/** ----------------------------------------------------------- */
	/** Any method whose parameter is String                        */
	/** ----------------------------------------------------------- */
	@Pointcut("args(java.lang.String)")
	public void pointCutStringId(){}
	
	/** ----------------------------------------------------------- */
	/** Any method whose parameter is RtrnTpModel                   */
	/** ----------------------------------------------------------- */
	@Pointcut("args(com.ns.spring.model.ui.RtrnTpModel)")
	public void pointCutRtrnTpParam(){}

	/** ----------------------------------------------------------- */
	/** Any class under this package                                */
	/** ----------------------------------------------------------- */
	@Pointcut("within(com.ns.spring.dao.template.*)")
	public void pointCutWithinDaoTemplate(){}

	/** ----------------------------------------------------------- */
	/** Any method in RmaController class                           */
	/** ----------------------------------------------------------- */
	@Pointcut("execution(* com.ns.spring.RmaController.*(..))")
	public void pointCutAllRmaController(){}

	/** ----------------------------------------------------------- */
	/** Any method starts with "get" under this package             */
	/** ----------------------------------------------------------- */
	@Pointcut("execution(* com.ns.spring.dao.template.*.get*(..))")
	public void pointCutDaoTemplate(){}

	/** ----------------------------------------------------------- */
	/** Any method in GenHbDaoImpl class                            */
	/** ----------------------------------------------------------- */
	@Pointcut("execution(* com.ns.spring.dao.GenHbDaoImpl.*(..))")
	public void pointCutGenDao(){}

	@Around("pointCutGenDao() || pointCutDaoTemplate()")
	public Object aroundDao(ProceedingJoinPoint jpt) throws Throwable {
		return aroundAdvice(jpt, "aroundDao");
	}

	@Around("pointCutStringId() && pointCutWithinDaoTemplate()")
	public Object aroundDaoTemplWStringParam(ProceedingJoinPoint jpt) throws Throwable {
		return aroundAdvice(jpt, "aroundDaoTemplWStringParam");
	}
	
	private Object aroundAdvice(ProceedingJoinPoint jpt, String methodName) {
		StopWatch sw = new StopWatch();
		sw.start();
		log.info("-------------------------(At "+methodName+") BEFORE-------------------------");		
		Object obj = null;
		try {
			obj = jpt.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		log.info("-------------------------(At "+methodName+") AFTER-------------------------");
		sw.stop();

		setLogMsg(jpt, sw);
		return obj;
	}
	
	private void setLogMsg(ProceedingJoinPoint jpt, StopWatch sw) {

		StringBuffer sb = new StringBuffer();
		sb.append("\n At " +getCurrTime());
		sb.append("\n Class: ");
		sb.append(jpt.getTarget().getClass().getName());
		sb.append("\n Method: ");
		sb.append(jpt.getSignature().getName());
		Object[] arr = jpt.getArgs();
		if (arr != null && arr.length > 0) {
			sb.append("\n Param(s): ");
			for (Object element : arr) {
				if (element != null) {
					sb.append(element).append(",");
				}
			}
			// Delete the last comma
			sb.deleteCharAt(sb.length() - 1);
		}
		sb.append("\n ================= Time consumed: ");
		sb.append(sw.getTotalTimeMillis());
		sb.append(" Milli Sec =================");
		log.info(sb.toString());
	}
	
	private String getCurrTime() {
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.S");
		return sdf.format(new Date());
	}
}

After Advise

aop-after-advice-header-img1

(1) Overview & (2) Configure
(3) Example

In APO, some process called AFTER the specified point (Point cut). There are three AFTER cases:

  • Just After something (always)
  • Only after error (Exception)
  • Only after no error

The typical example is a LOGGING. This example also does logging. The logging method called AFTER the point cut (After Advise).

To implements After Advise, AspectJ provides @After, @AfterThrowing and @AfterReturning annotation:

In the line 18:
The first example will be execute AFTER “Exception” occurs in GenHbDaoImpl class:
aop-after-error-example2
3.1. The first asterisk specified ANY return type
3.2. The second asterisk specified ANY method (name) in this class
3.3. The two period “..” specified ANY number and type of method parameter

In the line 25: The second example, the only different from the first example is that it will be execute after “NO Exception” case:
aop-after-no-error-example2
3.4.@AfterReturning” means after NO ERROR

In the line 32: The third example is executed always “AFTER” something.
aop-after-example2
3.5. In this case, ANY method in “RmaController” class starts with “backTo

package com.ns.spring.aop.advise;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectAfter {

	private static final Logger log = LoggerFactory.getLogger(AspectAfter.class);

	@AfterThrowing(pointcut = "execution(* com.ns.spring.dao.GenHbDaoImpl.*(..))", throwing = "error")
	public Object afterException(JoinPoint joinPoint, Throwable error) {
		log.info("(At afterException) Method name: " + joinPoint.getSignature().getName());
		log.info("(At afterException) Exception : " + error);
		return error;
	}

	@AfterReturning(pointcut = "execution(* com.ns.spring.dao.GenHbDaoImpl.*(..))", returning = "result")
	public Object afterNormalEnd(JoinPoint joinPoint, Object result) {
		log.info("(At afterNormalEnd) Method name: " + joinPoint.getSignature().getName());
		log.info("(At afterNormalEnd) Result: " + result);
		return result;
	}
	
	@After("execution(* com.ns.spring.RmaController.backTo*(..))")
	public void beforeAllRmaCtrlGoTo(JoinPoint pt){
		log.info("\n++++++++++++++++++++ Controller (Back To screen Transition) ++++++++++++++++++++ " +pt.getSignature().getName() + " called!");
		setLogMsg(pt);
	}
	
	private void setLogMsg(JoinPoint jpt) {

		StringBuffer sb = new StringBuffer();
		sb.append("\n Class: ");
		sb.append(jpt.getTarget().getClass().getName());
		sb.append("\n Method: ");
		sb.append(jpt.getSignature().getName());
		Object[] arr = jpt.getArgs();
		if (arr != null && arr.length > 0) {
			sb.append("\n Param(s): ");
			for (Object element : arr) {
				if (element != null) {
					sb.append(element).append(",");
				}
			}
			// Delete the last comma
			sb.deleteCharAt(sb.length() - 1);
		}
		log.info(sb.toString());
	}
}

Before Advise

aop-before-advice-header-img1

(1) Overview & (2) Configure
(3) Example
In APO, some process called BEFORE specified point (Point cut). The typical example is a LOGGING. This example also does logging. The logging method called BEFORE the point cut (Before Advise).

To implements Before Advise, AspectJ provides @Before annotation:
aop-before-exp-return-method3.1-3.3
In the line 16:
3.1. The first asterisk specified ANY return type
3.2. The second asterisk specified ANY method (name) in this class
3.3. The two period “..” specified ANY number and type of method parameter
aop-before-exp-return-method3.4
In the line 23:
3.4. The pointed asterisk specified ANY method name starts from “goTo
aop-before-exp-return-method3.5
In the line 30:
3.5. The method name is specified as “initList

package com.ns.spring.aop.advise;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectBefore {

	private static final Logger log = LoggerFactory.getLogger(AspectBefore.class);

	@Before("execution(* com.ns.spring.RmaController.*(..))")
	public void beforeAllRmaCtrl(JoinPoint pt){
		log.info("\n:::::::::::::::: Controller :::::::::::::::: " +pt.getSignature().getName() + " called!");
		Object[] arr = pt.getArgs();
		logMsg(arr);
	}
	
	@Before("execution(* com.ns.spring.RmaController.goTo*(..))")
	public void beforeAllRmaCtrlGoTo(JoinPoint pt){
		log.info("\n................ Controller (Screen Transition) ................ " +pt.getSignature().getName() + " called!");
		Object[] arr = pt.getArgs();
		logMsg(arr);
	}
	
	@Before("execution(* com.ns.spring.RmaController.initList(..))")
	public void beforeRmaCtrl_initList(JoinPoint pt){
		log.info("\n***************** Controller (Specified Method) ***************** " +pt.getSignature().getName() + " called!");
		Object[] arr = pt.getArgs();
		logMsg(arr);
	}
	
	private void logMsg(Object[] arr) {
		if( arr != null && arr.length > 0 ) {
			for( Object obj: arr) {
				log.info("Parameter value -> "+obj.toString());
			}
		}
	}
}

HQL and Native SQL in Hibernate

hiv-mvc-img
In Hibernate, there are number of ways to accessing a data: HQL, native SQL and others.
This section shows the implementation of HQL and native SQL.
These method are all defined in the DAO implementation classes.
hql-vs-sql-header-img1
The syntax of HQL is very much like SQL, but HQL is Hibernate object oriented.

SELECT * 
FROM RTRN_TP_RSN_CMBN  
WHERE rtrn_tp_cd ='01'
AND rtrn_rsn_cd ='001'

The above SQL is implemented as HQL as follow: (Source Code)

public RTRN_TP_RSN_CMBN getTpRsnCmbnByHql(String rtrnTpCd, String rtrnRsnCd) {

	String hqlStr = "from RTRN_TP_RSN_CMBN " 
			+ "where rtrn_tp_cd = :rtrnTpCd "
			+ "and   rtrn_rsn_cd = :rtrnRsnCd ";
	
	Query query = super.getCurrSession().createQuery(hqlStr);
	query.setParameter("rtrnTpCd", rtrnTpCd);
	query.setParameter("rtrnRsnCd", rtrnRsnCd);

	List<RTRN_TP_RSN_CMBN> list = query.list();
	if (list != null && list.size() > 0) {
		return (RTRN_TP_RSN_CMBN) list.get(0);
	}
	return null;
}

Native SQl: The result will be a list of Object Array.

SELECT 
 a1.rtrn_rsn_cd 
,a1.rtrn_rsn_nm 
,a1.sort_num 
,'' as rtrn_tp_cd 
FROM rtrn_rsn a1 
WHERE NOT EXISTS ( 
   SELECT '*' 
   FROM rtrn_rsn a2 
   JOIN rtrn_tp_rsn_cmbn cmb 
   ON a2.rtrn_rsn_cd = cmb.rtrn_rsn_cd 
   WHERE cmb.rtrn_tp_cd  = '02'
   AND a2.rtrn_rsn_cd = a1.rtrn_rsn_cd 
) 
ORDER BY a1.sort_num

The above SQL is implemented as follow: (Source Code)

public List<Object[]> getListRsnsUnBoundTpBySql(String tpCd) {
	
	StringBuilder sb = new StringBuilder();
	sb.append("\n");
	sb.append("SELECT \n");
	sb.append(" a1.rtrn_rsn_cd \n");
	sb.append(",a1.rtrn_rsn_nm \n");
	sb.append(",a1.sort_num \n");
	sb.append(",'' as rtrn_tp_cd \n");
	sb.append("FROM rtrn_rsn a1 \n");
	sb.append("WHERE NOT EXISTS ( \n");
	sb.append("   SELECT '*' \n");
	sb.append("   FROM rtrn_rsn a2 \n");
	sb.append("   JOIN rtrn_tp_rsn_cmbn cmb \n");
	sb.append("   ON a2.rtrn_rsn_cd = cmb.rtrn_rsn_cd \n");
	sb.append("   WHERE cmb.rtrn_tp_cd  = :tpCd \n");
	sb.append("   AND a2.rtrn_rsn_cd = a1.rtrn_rsn_cd \n");
	sb.append(") \n");
	sb.append("ORDER BY a1.sort_num");

	Query query = super.getCurrSession().createSQLQuery(sb.toString());
	query.setParameter("tpCd", tpCd);

	List<Object[]> list = query.list();
	return list;
}

Many to Many

many-to-many-header-img1
Many-to-Many mapping is usually implemented a Join Table, suppose we have Type and Reason table and Reason table for many-to-many mapping. Every Type can have more than one Reasons and every Reasons is a part of more than one Type.
2015-05-17_10h45_40

CREATE TABLE `rtrn_tp` (
	`rtrn_tp_cd` VARCHAR(20) NOT NULL DEFAULT '',
	`rtrn_tp_nm` VARCHAR(100) NULL DEFAULT NULL,
	`sort_num` INT(11) UNSIGNED NOT NULL DEFAULT '0',
	PRIMARY KEY (`rtrn_tp_cd`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `rtrn_rsn` (
	`rtrn_rsn_cd` VARCHAR(20) NOT NULL DEFAULT '',
	`rtrn_rsn_nm` VARCHAR(100) NULL DEFAULT NULL,
	`sort_num` INT(11) UNSIGNED NOT NULL DEFAULT '0',
	PRIMARY KEY (`rtrn_rsn_cd`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `rtrn_tp_rsn_cmbn` (
	`rtrn_tp_cd` VARCHAR(20) NOT NULL DEFAULT '',
	`rtrn_rsn_cd` VARCHAR(20) NOT NULL DEFAULT '',
	PRIMARY KEY (`rtrn_tp_cd`, `rtrn_rsn_cd`),
	INDEX `fk_rtrn_rsn` (`rtrn_rsn_cd`),
	CONSTRAINT `fk_rtrn_rsn` FOREIGN KEY (`rtrn_rsn_cd`) REFERENCES `rtrn_rsn` (`rtrn_rsn_cd`),
	CONSTRAINT `fk_rtrn_tp` FOREIGN KEY (`rtrn_tp_cd`) REFERENCES `rtrn_tp` (`rtrn_tp_cd`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

There is a Join Table, RTRN_TP_RSN_CMBN, only have the relationship between Type (RTRN_TP) and Reason (RTRN_RSN)

package com.ns.spring.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "RTRN_TP")
public class RTRN_TP {

	@Id
	@Column(name = "rtrn_tp_cd")
	private String rtrnTpCd;

	@Column(name = "rtrn_tp_nm")
	private String rtrnTpNm;

	@Column(name = "sort_num")
	private int sortNum;

	@ManyToMany(targetEntity = RTRN_RSN.class, cascade = { CascadeType.ALL })
	@JoinTable(name = "RTRN_TP_RSN_CMBN", 
				joinColumns = { @JoinColumn(name = "rtrn_tp_cd") }, 
				inverseJoinColumns = { @JoinColumn(name = "rtrn_rsn_cd") })
	private Set<RTRN_RSN> rtrnRsns = new HashSet<RTRN_RSN>();

	public String getRtrnTpCd() {
		return rtrnTpCd;
	}

	public void setRtrnTpCd(String rtrnTpCd) {
		this.rtrnTpCd = rtrnTpCd;
	}

	public String getRtrnTpNm() {
		return rtrnTpNm;
	}

	public void setRtrnTpNm(String rtrnTpNm) {
		this.rtrnTpNm = rtrnTpNm;
	}

	public int getSortNum() {
		return sortNum;
	}

	public void setSortNum(int sortNum) {
		this.sortNum = sortNum;
	}

	public Set<RTRN_RSN> getRtrnRsns() {
		return rtrnRsns;
	}

	public void setRtrnRsns(Set<RTRN_RSN> rtrnRsns) {
		this.rtrnRsns = rtrnRsns;
	}

	public String toString() {
		return "Type Code:" + this.rtrnTpCd + ", Name:" + this.rtrnTpNm;
	}
}
package com.ns.spring.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "RTRN_RSN")
public class RTRN_RSN {

	@Id
	@Column(name = "rtrn_rsn_cd")
	private String rtrnRsnCd;
	
	@Column(name = "rtrn_rsn_nm")	
	private String rtrnRsnNm;
	
	@Column(name = "sort_num")	
	private int sortNum;

	public String getRtrnRsnCd() {
		return rtrnRsnCd;
	}

	public void setRtrnRsnCd(String rtrnRsnCd) {
		this.rtrnRsnCd = rtrnRsnCd;
	}

	public String getRtrnRsnNm() {
		return rtrnRsnNm;
	}

	public void setRtrnRsnNm(String rtrnRsnNm) {
		this.rtrnRsnNm = rtrnRsnNm;
	}

	public int getSortNum() {
		return sortNum;
	}

	public void setSortNum(int sortNum) {
		this.sortNum = sortNum;
	}

	public String toString() {
		return "Reason Code:" + this.rtrnRsnCd + ", Name:" + this.rtrnRsnNm;
	}
}
package com.ns.spring.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "RTRN_TP_RSN_CMBN")
public class RTRN_TP_RSN_CMBN implements Serializable {

	private static final long serialVersionUID = 4677612728077605784L;

	@Id
	@Column(name = "rtrn_tp_cd")
	private String rtrnTpCd;
	
	@Id
	@Column(name = "rtrn_rsn_cd")
	private String rtrnRsnCd;
	
	public String getRtrnTpCd() {
		return rtrnTpCd;
	}

	public void setRtrnTpCd(String rtrnTpCd) {
		this.rtrnTpCd = rtrnTpCd;
	}

	public String getRtrnRsnCd() {
		return rtrnRsnCd;
	}

	public void setRtrnRsnCd(String rtrnRsnCd) {
		this.rtrnRsnCd = rtrnRsnCd;
	}
	
	public String toString() {
		return "Type" + this.rtrnTpCd + ", Reason:" + this.rtrnRsnCd;
	}
}
@RequestMapping(value = "/cUrlValAttrbSubTpRsn01Jsp", params = "toBoundRsnList", method = RequestMethod.POST)
public ModelAndView toBoundRsnList(@ModelAttribute("RtrnTpModel") RtrnTpModel tp, ModelMap model, HttpServletRequest req) {

	String tpSelected = (String) req.getSession().getAttribute(CONST.TP_SELECTED.getVal());
	if (CommonBL.hasValue(tpSelected)) {
		RTRN_TP tpObj = this.tpSvc.findById(tpSelected);
		if (tpObj != null) {
			List<String> selectedCheckBox = TpRsnBL.getRsnToBound(tp, req);
			if (!CommonBL.isEmpty(selectedCheckBox)) {
				Set<RTRN_RSN> rtrnRsns = new HashSet<RTRN_RSN>();
				for (String code : selectedCheckBox) {
					RTRN_RSN rsnObj = this.rsnSvc.findById(code);
					if (rsnObj != null) {
						rtrnRsns.add(rsnObj);
						tpObj.setRtrnRsns(rtrnRsns);
						this.tpSvc.saveOrUpdate(tpObj);
					}
				}
			}
		}
	}
	return getMVSubTpRsn01(model, req);
}

This is how to use Many to Many in Spring MVC.

One to Many – Composite Primary Key

one-to-many-header-img1
In Hibernate, you need to have special field in order to accomplish a composite primary key to make it as a single key object:
one-to-many-header-comppk1
To handle composite primary key, you need to user an extra class “RmaLinePk”.
2015-06-01_07h41_10

2015-05-17_10h08_28

CREATE TABLE `rma_hdr` (
	`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
	`rma_num` VARCHAR(20) NOT NULL DEFAULT '',
	`rma_hdr_sts_cd` VARCHAR(20) NULL DEFAULT NULL,
	`rtrn_tp_cd` VARCHAR(20) NOT NULL DEFAULT '',
	`rtrn_rsn_cd` VARCHAR(20) NULL DEFAULT NULL,
	`sell_to_cust_cd` VARCHAR(20) NULL DEFAULT NULL,
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=27
;
CREATE TABLE `rma_line` (
	`rma_num` VARCHAR(20) NOT NULL DEFAULT '',
	`rma_line_num` VARCHAR(20) NOT NULL DEFAULT '',
	`rma_line_sts_cd` VARCHAR(20) NOT NULL DEFAULT '',
	`mdse_cd` VARCHAR(20) NOT NULL DEFAULT '',
	`qty` INT(11) UNSIGNED NOT NULL DEFAULT '0',
	`rma_id` INT(11) UNSIGNED NOT NULL,
	PRIMARY KEY (`rma_num`, `rma_line_num`),
	INDEX `rma_id` (`rma_id`),
	CONSTRAINT `rma_line_ibfk_1` FOREIGN KEY (`rma_id`) REFERENCES `rma_hdr` (`id`) ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
package com.ns.spring.model;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.ns.spring.model.ui.RmaHdrModel;

/**
 * Entity bean with JPA annotations Hibernate provides JPA implementation
 */
@Entity
@Table(name = "RMA_HDR")
public class RMA_HDR {

	@Id
	@Column(name = "id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	@Column(name = "rma_num")
	private String rmaNum;
	
	@Column(name = "rma_hdr_sts_cd")
	private String rmaHdrStsCd;

	@Column(name = "rtrn_tp_cd")
	private String rtrnTpCd;

	@Column(name = "rtrn_rsn_cd")
	private String rtrnRsnCd;

	@Column(name = "sell_to_cust_cd")
	private String sellToCustCd;

	@OneToMany(mappedBy = "rmaHdr", targetEntity = RMA_LINE.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	private List<RMA_LINE> rmaLines;
	
	public RMA_HDR(){}

	public RMA_HDR(RmaHdrModel hdr) {
		this.id = hdr.getId();
		this.rmaNum = hdr.getRmaNum();
		this.rmaHdrStsCd = hdr.getRmaHdrStsCd();
		this.rtrnTpCd = hdr.getRtrnTpCd();
		this.rtrnRsnCd = hdr.getRtrnRsnCd();
		this.sellToCustCd = hdr.getSellToCustCd();
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getRmaNum() {
		return rmaNum;
	}

	public void setRmaNum(String rmaNum) {
		this.rmaNum = rmaNum;
	}

	public String getRmaHdrStsCd() {
		return rmaHdrStsCd;
	}

	public void setRmaHdrStsCd(String rmaHdrStsCd) {
		this.rmaHdrStsCd = rmaHdrStsCd;
	}

	public String getRtrnTpCd() {
		return rtrnTpCd;
	}

	public void setRtrnTpCd(String rtrnTpCd) {
		this.rtrnTpCd = rtrnTpCd;
	}

	public String getRtrnRsnCd() {
		return rtrnRsnCd;
	}

	public void setRtrnRsnCd(String rtrnRsnCd) {
		this.rtrnRsnCd = rtrnRsnCd;
	}

	public String getSellToCustCd() {
		return sellToCustCd;
	}

	public void setSellToCustCd(String sellToCustCd) {
		this.sellToCustCd = sellToCustCd;
	}

	public List<RMA_LINE> getRmaLines() {
		return rmaLines;
	}

	public void setRmaLines(List<RMA_LINE> rmaLines) {
		this.rmaLines = rmaLines;
	}
	
	public String toString() {
		return "ID:" + this.id + ", RMA_NUM:" + this.rmaNum + ", Status:" + this.rmaHdrStsCd + ", Type:" + this.rtrnTpCd + ", Reason:" + this.rtrnRsnCd;
	}
}
@Id
private RmaLinePk rmaLinePk;

Above:In “many side”, it uses the primary key field

package com.ns.spring.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.JoinColumn;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Entity bean with JPA annotations Hibernate provides JPA implementation
 */
@Entity
@Table(name = "RMA_LINE")
public class RMA_LINE {

	@Id
	private RmaLinePk rmaLinePk;

	@Column(name = "rma_line_sts_cd")
	private String rmaLineStsCd;

	@Column(name = "mdse_cd")
	private String mdseCd;

	@Column(name = "qty")
	private int qty;

	@ManyToOne
	@JoinColumn(name = "rma_id")
	private RMA_HDR rmaHdr;
	
	public RMA_LINE(){}

	public RmaLinePk getRmaLinePk() {
		return rmaLinePk;
	}

	public void setRmaLinePk(RmaLinePk rmaLinePk) {
		this.rmaLinePk = rmaLinePk;
	}

	public String getRmaLineStsCd() {
		return rmaLineStsCd;
	}

	public void setRmaLineStsCd(String rmaLineStsCd) {
		this.rmaLineStsCd = rmaLineStsCd;
	}

	public String getMdseCd() {
		return mdseCd;
	}

	public void setMdseCd(String mdseCd) {
		this.mdseCd = mdseCd;
	}

	public RMA_HDR getRmaHdr() {
		return rmaHdr;
	}

	public int getQty() {
		return qty;
	}

	public void setQty(int qty) {
		this.qty = qty;
	}

	public void setRmaHdr(RMA_HDR rmaHdr) {
		this.rmaHdr = rmaHdr;
	}

	public String toString() {
		return "PK:" + this.rmaLinePk.toString() + ", Line Status:" + this.rmaLineStsCd + ", Mdse:" + this.mdseCd + ", Qty:" + this.qty;
	}
}
private String rma_num;
private String rma_line_num;

The real primary keys which are composite defines in the following class: RmaLinePk.java

package com.ns.spring.model;

import java.io.Serializable;

import javax.persistence.Embeddable;

@Embeddable
public class RmaLinePk implements Serializable {

	private static final long serialVersionUID = -403250971215465050L;

	private String rma_num;

	private String rma_line_num;

	public RmaLinePk() {
	}

	public RmaLinePk(String rma_num, String rma_line_num) {
		this.rma_num = rma_num;
		this.rma_line_num = rma_line_num;
	}

	public String getRma_num() {
		return rma_num;
	}

	public void setRma_num(String rma_num) {
		this.rma_num = rma_num;
	}

	public String getRma_line_num() {
		return rma_line_num;
	}

	public void setRma_line_num(String rma_line_num) {
		this.rma_line_num = rma_line_num;
	}

	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		RmaLinePk other = (RmaLinePk) obj;
		if (rma_num == null) {
			if (other.rma_num != null) {
				return false;
			}
		} else if (!rma_num.equals(other.rma_num)) {
			return false;
		}
		if (rma_line_num == null) {
			if (other.rma_line_num != null) {
				return false;
			}
		} else if (!rma_line_num.equals(other.rma_line_num)) {
			return false;
		}
		return true;
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((rma_num == null) ? 0 : rma_num.hashCode());
		result = prime * result	+ ((rma_line_num == null) ? 0 : rma_line_num.hashCode());
		return result;
	}

	public String toString() {
		return "RmaLinePk:RMA#:" + this.rma_num + ", Line#:" + this.rma_line_num;
	}
}

This is how the data is stored in controller: See also Submitting a collection.

public ModelAndView submitLine(@ModelAttribute("rmaLineListModel") RmaLineListModel rmaLineListModel, Model model, HttpServletRequest req) {

	String rmaNum = rmaLineListModel.getRmaNum();
	List<RmaLineModel> currList = rmaLineListModel.getRmaLineModelList();
	if (!CommonBL.isEmpty(currList)) {
		for (RmaLineModel obj : currList) {
			String rmaLineNum = obj.getRmaLineNum();
			List<RmaLineModel> rmaLinsList = RmaDtlBL.getRmaLineListWithCdTblNm(rmaNum, rmaLineNum);
			if (CommonBL.isEmpty(rmaLinsList)) {
				// New Record
				this.rmaLineSvc.save(RmaDtlBL.getRmaLineObj(obj, rmaNum, rmaLineNum));
			}
		}
	}
	List<RmaLineModel> rmaLineModelList = RmaDtlBL.getRmaLineListWithCdTblNm(rmaNum, null);
	return getMVSubLine01(rmaNum, rmaLineModelList, req);
}

This method passes the object to be inserted/updated

public static RMA_LINE getRmaLineObj(RmaLineModel obj, String rmaNum, String rmaLineNum) {
	RMA_LINE rmaLine = new RMA_LINE();
	rmaLine.setRmaLinePk(new RmaLinePk(rmaNum, rmaLineNum));
	rmaLine.setRmaHdr(RmaBL.getRmaHdrObj(rmaNum));
	rmaLine.setRmaLineStsCd(getStsCd(obj.getRmaLineStsCd()));
	rmaLine.setMdseCd(obj.getMdseCd());
	rmaLine.setQty(obj.getQty());
	return rmaLine;
}

Static Factory Pattern

design-pattern-hdr_img2
It is always good to use Design Pattern in your application, although it is not required, in order to avoid spaghetti. One of popular design pattern is Factory pattern. The concept is similar to DI (Dependency Injection). In this example, I use Static Factory pattern.
stat-facto-pattern-diagram1
With Factory pattern, you can instantiate a class with “new” (Source Code)

@RequestMapping("/cUrlValSubLine01Jsp/{rmaNum}")
public ModelAndView goToSubLine01Jsp(@PathVariable("rmaNum") String rmaNum,	Model model, HttpServletRequest req) {
	List<RmaLineModel> rmaLineModelList = RmaDtlBL.getRmaLineListWithCdTblNm(rmaNum, null);
	return getMVSubLine01(rmaNum, rmaLineModelList, req);
}

This method is being called to set ModelAndView object, and one of them is the list of code for auto-complete feature

private ModelAndView getMVSubLine01(String rmaNum, List<RmaLineModel> rmaLinsList, HttpServletRequest req) {

	RmaLineListModel newModel = new RmaLineListModel(rmaNum, rmaLinsList);

	ModelAndView modelAndView = new ModelAndView(VIEW.SUB_LINE_01.getVal());
	modelAndView.addObject(CONST.FORM_KEY.getVal(), newModel);
	// For item detail list
	modelAndView.addObject(CONST.LINE_LIST_MODEL.getVal(), newModel);
	// Setup Status drop down line the item list
	modelAndView.addObject(CONST.HDR_STS_LIST.getVal(), getList(CONST.HDR_STS_LIST.getVal()));
	// Item list for AJAX Auto complete
	req.getSession().setAttribute(CONST.MDSE_LIST.getVal(), getList(CONST.MDSE_LIST.getVal()));
	return modelAndView;
}

Above line 12 calls this method:

private List<?> getList(String key) {
	BLFactory fact = BLFactory.getInstance(key);
	return fact.getList();
}

Above line 12 calls this method:

package com.ns.spring.common.gen;

import java.util.List;

import com.ns.spring.constant.RtrnConstant;

public class BLFactory implements RtrnConstant {

	private BLService svc;

	private BLFactory(String param) {
		if (CONST.HDR_STS_LIST.getVal().equals(param)) {
			this.svc = new RtrnCommonBL();
		} else if (CONST.TP_LIST.getVal().equals(param)) {
			this.svc = new TpCommonBL();
		} else if (CONST.MDSE_LIST.getVal().equals(param)) {
			this.svc = new MdseCommonBL();
		}
	}

	// static factory
	public static BLFactory getInstance(String param) {
		return new BLFactory(param);
	}

	public List<?> getList() {
		return this.svc.getList();
	}
}

This factory class returns static instance via interface BLService.

package com.ns.spring.common.gen;

import java.util.List;

public interface BLService {
	public List<?> getList();
}
package com.ns.spring.common.gen;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

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

import com.ns.spring.common.CommonBL;
import com.ns.spring.constant.RtrnConstant.CONST;
import com.ns.spring.model.MDSE;
import com.ns.spring.model.RMA_HDR_STS;
import com.ns.spring.model.RTRN_TP;
import com.ns.spring.model.ui.MdseModel;
import com.ns.spring.model.ui.RmaHdrStsModel;
import com.ns.spring.model.ui.RtrnTpModel;
import com.ns.spring.service.template.MdseService;
import com.ns.spring.service.template.RmaHdrStsService;
import com.ns.spring.service.template.RtrnTpService;

@Component
public class MdseCommonBL implements BLService {

	private static MdseService mdseSvc;
	
	@Autowired(required = true)
	public void setMdseSvc(MdseService mdseSvc) {
		this.mdseSvc = mdseSvc;
	}

	@Override
	public List<MdseModel> getList() {
		List<MdseModel> mdseModelList = new ArrayList<>();
		List<MDSE> mdseList = mdseSvc.findAll();
		if (!CommonBL.isEmpty(mdseList)) {
			for (int i = 0; i < mdseList.size(); i++) {
				MDSE obj = (MDSE) mdseList.get(i);
				mdseModelList.add(new MdseModel(i, obj.getMdseCd()));
			}
		}
		return mdseModelList;
	}
}

getList() eventually calls findAll() that returns all record from MDSE table.

Generic DAO with Custom Method

db-chain1
Instead of having DAO for each entity, calling Generic DAO simplify the program.
Unlike the template classes with empty method that can only use the method specified in Generic DAO classes, this example use additional customized method (Source Code)

2015-05-30_11h15_25

2015-05-30_11h17_10

package com.ns.spring.dao.template;

import java.util.List;

import com.ns.spring.dao.GenHbDao;
import com.ns.spring.model.RMA_LINE;

public interface RmaLineDao extends GenHbDao<RMA_LINE, String> {

	public List<Object[]> getListRmaLineBySql(String rmaNum, String rmaLineNum);
	public RMA_LINE getRmaLineByHql(String rmaNum, String rmaLineNum);
}
package com.ns.spring.dao.template.impl;
package com.ns.spring.dao.template.impl;

import java.util.List;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;

import com.ns.spring.dao.GenHbDaoImpl;
import com.ns.spring.dao.template.RmaLineDao;
import com.ns.spring.model.RMA_LINE;

@Repository
public class RmaLineDaoImpl extends GenHbDaoImpl<RMA_LINE, String> implements RmaLineDao {

	@SuppressWarnings("unchecked")
	@Override
	public List<Object[]> getListRmaLineBySql(String rmaNum, String rmaLineNum) {
		
		StringBuilder sb = new StringBuilder();
		sb.append("\n");
		sb.append("SELECT\n");
		sb.append("  rl.rma_num\n");
		sb.append(" ,rl.rma_line_num\n");
		sb.append(" ,rl.rma_line_sts_cd\n");
		sb.append(" ,sts.rma_hdr_sts_nm\n");
		sb.append(" ,rl.mdse_cd\n");
		sb.append(" ,mdse.mdse_nm\n");
		sb.append(" ,rl.qty\n");
		sb.append(" ,rl.rma_id\n");
		sb.append("FROM RMA_LINE rl\n");
		sb.append("LEFT OUTER JOIN RMA_HDR_STS sts\n");
		sb.append("ON rl.rma_line_sts_cd = sts.rma_hdr_sts_cd\n");
		sb.append("LEFT OUTER JOIN MDSE mdse\n");
		sb.append("ON rl.mdse_cd = mdse.mdse_cd\n");
		sb.append("WHERE rl.rma_num = :rmaNum\n");
		if (rmaLineNum != null) {
			sb.append("AND rl.rma_line_num = :rmaLineNum\n");
		}		
		sb.append("ORDER BY\n");
		sb.append("  rl.rma_num\n");
		sb.append(" ,rl.rma_line_num\n");

		Query query = super.getCurrSession().createSQLQuery(sb.toString());
		query.setParameter("rmaNum", rmaNum);
		if (rmaLineNum != null) {
			query.setParameter("rmaLineNum", rmaLineNum);
		}
		List<Object[]> list = query.list();
		return list;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public RMA_LINE getRmaLineByHql(String rmaNum, String rmaLineNum) {

		String hqlStr = "from RMA_LINE " 
				+ "where rma_num = :rmaNum "
				+ "and   rma_line_num = :rmaLineNum ";
		
		Query query = super.getCurrSession().createQuery(hqlStr);
		query.setParameter("rmaNum", rmaNum);
		query.setParameter("rmaLineNum", rmaLineNum);

		List<RMA_LINE> list = query.list();
		if (list != null && list.size() > 0) {
			return (RMA_LINE) list.get(0);
		}
		return null;
	}
}

DAO interface, and it’s implementation has two additional methods:
1. Native SQL with its reesult
2. HQL with its reesult

package com.ns.spring.service.template;

import java.util.List;

import com.ns.spring.model.RMA_LINE;
import com.ns.spring.service.GenHbService;

public interface RmaLineService extends GenHbService<RMA_LINE, String> {

	public List<Object[]> getListRmaLineBySql(String rmaNum, String rmaLineNum);
	public RMA_LINE getRmaLineByHql(String rmaNum, String rmaLineNum);
}
package com.ns.spring.service.template.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ns.spring.dao.GenHbDao;
import com.ns.spring.dao.template.RmaLineDao;
import com.ns.spring.model.RMA_LINE;
import com.ns.spring.service.GenHbServiceImpl;
import com.ns.spring.service.template.RmaLineService;

@Service("rmaLineService")
public class RmaLineServiceImpl extends GenHbServiceImpl<RMA_LINE, String> implements RmaLineService {

	private RmaLineDao dao;

	public RmaLineServiceImpl() {
	}

	@Autowired
	public RmaLineServiceImpl(@Qualifier("rmaLineDaoImpl") GenHbDao<RMA_LINE, String> genericDao) {
		super(genericDao);
		this.dao = (RmaLineDao) genericDao;
	}
	
	@Override
	@Transactional
	public List<Object[]> getListRmaLineBySql(String rmaNum, String rmaLineNum) {
		return this.dao.getListRmaLineBySql(rmaNum, rmaLineNum);
	}

	@Override
	@Transactional
	public RMA_LINE getRmaLineByHql(String rmaNum, String rmaLineNum) {
		return this.dao.getRmaLineByHql(rmaNum, rmaLineNum);
	}
}

DAO service interface, and it’s implementation calling super class, and two custom method defined in RmaLineDaoImpl.