From 701dcb2593a528b6f4c899144075d289e5b298a2 Mon Sep 17 00:00:00 2001
From: Gustavo Martin Morcuende <gu.martinm@gmail.com>
Date: Fri, 1 Jul 2016 22:58:59 +0200
Subject: [PATCH] More JPA stuff

---
 SpringJava/JPA/context.xml                         |  53 ++++++++++
 SpringJava/JPA/pom.xml                             |  16 +--
 .../de/spring/persistence/example/domain/Ad.java   | 108 ++++++++++-----------
 .../persistence/example/domain/AdDescription.java  | 100 +++++++++++++++++++
 .../repository/AdDescriptionRepository.java        |  13 +++
 .../example/repository/AdRepository.java           |  13 ++-
 .../de/spring/rest/controllers/AdController.java   |  10 +-
 .../datasource-configuration.xml                   |  33 ++-----
 .../spring-configuration/jpa-configuration.xml     |   4 +-
 SpringJava/JPA/src/main/webapp/WEB-INF/web.xml     |  20 ++++
 10 files changed, 278 insertions(+), 92 deletions(-)
 create mode 100644 SpringJava/JPA/context.xml
 create mode 100644 SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/AdDescription.java
 create mode 100644 SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdDescriptionRepository.java

diff --git a/SpringJava/JPA/context.xml b/SpringJava/JPA/context.xml
new file mode 100644
index 0000000..e053ee3
--- /dev/null
+++ b/SpringJava/JPA/context.xml
@@ -0,0 +1,53 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- The contents of this file will be loaded for each web application -->
+<Context>
+
+    <!-- Default set of monitored resources -->
+    <WatchedResource>WEB-INF/web.xml</WatchedResource>
+
+    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
+    <!--
+    <Manager pathname="" />
+    -->
+
+    <!-- Uncomment this to enable Comet connection tacking (provides events
+         on session expiration as well as webapp lifecycle) -->
+    <!--
+    <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
+    -->
+
+
+
+    <Resource auth="Container"
+		driverClassName="com.mysql.jdbc.Driver"
+    	logAbandoned="true" 
+		maxTotal="300" 
+		maxIdle="5" 
+		maxWaitMillis="5000" 
+		name="jdbc/example"
+    	password="root" 
+		removeAbandonedOnBorrow="true" 
+		removeAbandonedOnMaintenance="true" 
+		type="javax.sql.DataSource"
+    	url="jdbc:mysql://localhost:3306/mybatis_example?allowMultiQueries=true&amp;autoReconnect=true&amp;characterEncoding=UTF-8"
+    	username="root" 
+		validationQuery="select 1" />
+
+
+</Context>
diff --git a/SpringJava/JPA/pom.xml b/SpringJava/JPA/pom.xml
index c8a763a..1c800b3 100644
--- a/SpringJava/JPA/pom.xml
+++ b/SpringJava/JPA/pom.xml
@@ -13,8 +13,8 @@
 		<url>https://gumartinm.name/</url>
 	</organization>
 	<scm>
-		<developerConnection>scm:git:http://git.gumartinm.name/Spring/JPA</developerConnection>
-		<url>http://git.gumartinm.name/Spring/JPA</url>
+		<developerConnection>scm:git:https://git.gumartinm.name/Spring/JPA</developerConnection>
+		<url>https://git.gumartinm.name/Spring/JPA</url>
 	</scm>
 
 	<properties>
@@ -104,18 +104,18 @@
 				</exclusion>
 			</exclusions>
 		</dependency>
+		<dependency>
+			<groupId>javax.inject</groupId>
+  			<artifactId>javax.inject</artifactId>
+  			<version>1</version>
+		</dependency>
 		
 		<dependency>
 			<groupId>cglib</groupId>
 			<artifactId>cglib</artifactId>
 			<version>2.2.2</version>
 		</dependency>
-
-		<dependency>
-			<groupId>com.mchange</groupId>
-			<artifactId>c3p0</artifactId>
-			<version>0.9.5.2</version>
-		</dependency>
+		
 
 		<!-- Required JPA dependencies with hibernate -->
 		<dependency>
diff --git a/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/Ad.java b/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/Ad.java
index e29ec68..6784538 100644
--- a/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/Ad.java
+++ b/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/Ad.java
@@ -2,43 +2,88 @@ package de.spring.persistence.example.domain;
 
 import java.io.Serializable;
 import java.time.LocalDateTime;
+import java.util.Set;
 
 import javax.persistence.Column;
+import javax.persistence.Convert;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.NamedNativeQueries;
+import javax.persistence.NamedNativeQuery;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 
+import de.spring.persistence.converters.LocalDateTimeAttributeConverter;
+
 @Entity
-@Table(schema = "mybatis_example")
+@Table(name="ad", schema="mybatis_example")
+// 1. Named query is JPL. It is portable.
+// 2. Instead of annotating the domain class we should be using @Query annotation at the query method
+//    because it should be cleaner :)
+//    So you'd better use @Query.
+//http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
+//See: de.spring.persistence.example.repository.AdRepository 
+@NamedQueries(
+		{
+			@NamedQuery(
+			name="Ad.findByIdQuery",
+			query="select a FROM AD a WHERE a.id = :id")
+		}
+	
+)
+// 1. Native query IS NOT JPL. It is not portable and it is written directly in the native language
+//    of the store. We can use special features at the cost of portability.
+// 2. Instead of annotating the domain class we should be using @Query annotation at the query method
+// 	  because it should be cleaner :)
+//    So you'd better use @Query.
+//    http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
+//    See: de.spring.persistence.example.repository.AdRepository 
+@NamedNativeQueries(
+	{
+		@NamedNativeQuery(
+			name="Ad.findByIdNativeQuery",
+			query="SELECT * FROM AD WHERE AD.ID = :id",
+			resultClass=Ad.class)
+	}		
+)
 public class Ad implements Serializable {
 
 	@Id
 	@GeneratedValue(strategy=GenerationType.IDENTITY)
-	@Column(name = "id", updatable = false, nullable = false)
+	@Column(name="id", updatable=false, nullable=false)
 	private Long id;
 	
+	@OneToMany(mappedBy="ad", fetch=FetchType.LAZY, targetEntity=AdDescription.class)
+	private Set<AdDescription> adDescriptions;
+	
 	@Max(60)
+	@Column(name="company_id")
 	private Long companyId;
 	
 	@Max(40)
-	@Column
+	@Column(name="company_categ_id")
 	private Long companyCategId;
 	
-	@Size(min=2, max=240)
-	@Column
+	@Size(min=2, max=255)
+	@Column(name="ad_mobile_image")
 	private String adMobileImage;
 
 	@NotNull
-	@Column(nullable = false)
+	@Convert(converter=LocalDateTimeAttributeConverter.class)
+	@Column(name="created_at", nullable=false)
 	private LocalDateTime createdAt;
 	
 	@NotNull
-	@Column(nullable = false)
+	@Convert(converter=LocalDateTimeAttributeConverter.class)
+	@Column(name="updated_at", nullable = false)
 	private LocalDateTime updatedAt;
 
 	// It will be used by JPA when filling the property fields with data coming from data base.
@@ -79,53 +124,4 @@ public class Ad implements Serializable {
 	public LocalDateTime getUpdatedAt() {
 		return updatedAt;
 	}
-	
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((adMobileImage == null) ? 0 : adMobileImage.hashCode());
-		result = prime * result + ((companyCategId == null) ? 0 : companyCategId.hashCode());
-		result = prime * result + ((companyId == null) ? 0 : companyId.hashCode());
-		result = prime * result + ((createdAt == null) ? 0 : createdAt.hashCode());
-		result = prime * result + ((updatedAt == null) ? 0 : updatedAt.hashCode());
-		return result;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		Ad other = (Ad) obj;
-		if (adMobileImage == null) {
-			if (other.adMobileImage != null)
-				return false;
-		} else if (!adMobileImage.equals(other.adMobileImage))
-			return false;
-		if (companyCategId == null) {
-			if (other.companyCategId != null)
-				return false;
-		} else if (!companyCategId.equals(other.companyCategId))
-			return false;
-		if (companyId == null) {
-			if (other.companyId != null)
-				return false;
-		} else if (!companyId.equals(other.companyId))
-			return false;
-		if (createdAt == null) {
-			if (other.createdAt != null)
-				return false;
-		} else if (!createdAt.equals(other.createdAt))
-			return false;
-		if (updatedAt == null) {
-			if (other.updatedAt != null)
-				return false;
-		} else if (!updatedAt.equals(other.updatedAt))
-			return false;
-		return true;
-	}
 }
diff --git a/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/AdDescription.java b/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/AdDescription.java
new file mode 100644
index 0000000..a3b7d16
--- /dev/null
+++ b/SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/AdDescription.java
@@ -0,0 +1,100 @@
+package de.spring.persistence.example.domain;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+@Entity
+@Table(name="ad_description", schema="mybatis_example")
+public class AdDescription implements Serializable {
+
+	@Id
+	@GeneratedValue(strategy=GenerationType.IDENTITY)
+	@Column(name="id", updatable=false, nullable=false)
+	private Long id;
+	
+	@NotNull
+	@ManyToOne(optional=false)
+	@JoinColumn(name="ad_id", referencedColumnName="id")
+	private Ad ad;
+	
+	@NotNull
+	@Max(60)
+	@Column(name="language_id")
+	private Long languageId;
+	
+	@NotNull
+	@Size(min=2, max=255)
+	@Column(name="ad_name")
+	private String adName;
+
+	@NotNull
+	@Size(min=2, max=255)
+	@Column(name="ad_description")
+	private String adDescription;
+	
+	@NotNull
+	@Size(min=2, max=500)
+	@Column(name="ad_mobile_text")
+	private String adMobileText;
+	
+	@NotNull
+	@Size(min=2, max=3000)
+	@Column(name="ad_link")
+	private String adLink;
+	
+	// It will be used by JPA when filling the property fields with data coming from data base.
+	protected AdDescription() {
+
+	}
+
+	// It will be used by my code (for example by Unit Tests)
+	public AdDescription(Long id, Ad ad, Long languageId, String adName, String adDescription,
+			String adMobileText, String adLink) {
+		this.id = id;
+		this.languageId = languageId;
+		this.ad = ad;
+		this.adName = adName;
+		this.adDescription = adDescription;
+		this.adMobileText = adMobileText;
+		this.adLink = adLink;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public Ad getAd() {
+		return ad;
+	}
+
+	public Long getLanguageId() {
+		return languageId;
+	}
+
+	public String getAdName() {
+		return adName;
+	}
+
+	public String getAdDescription() {
+		return adDescription;
+	}
+
+	public String getAdMobileText() {
+		return adMobileText;
+	}
+
+	public String getAdLink() {
+		return adLink;
+	}
+}
diff --git a/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdDescriptionRepository.java b/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdDescriptionRepository.java
new file mode 100644
index 0000000..120b2c8
--- /dev/null
+++ b/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdDescriptionRepository.java
@@ -0,0 +1,13 @@
+package de.spring.persistence.example.repository;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import de.spring.persistence.example.domain.Ad;
+import de.spring.persistence.example.domain.AdDescription;
+
+public interface AdDescriptionRepository extends PagingAndSortingRepository<AdDescription, Long> {
+
+	// Custom Query method
+	Page<AdDescription> findByAd(Ad ad);
+}
diff --git a/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdRepository.java b/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdRepository.java
index 8230c43..b625fed 100644
--- a/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdRepository.java
+++ b/SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdRepository.java
@@ -1,9 +1,20 @@
 package de.spring.persistence.example.repository;
 
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.query.Param;
 
 import de.spring.persistence.example.domain.Ad;
 
 public interface AdRepository extends PagingAndSortingRepository<Ad, Long> {
-
+	
+	// Named Native Query (using the native language of the store) It is not portable.
+	// See de.spring.persistence.example.domain.Ad
+	@Query(value="SELECT * FROM AD WHERE AD.ID = :id", nativeQuery=true)
+	Ad findByIdNativeQuery(@Param("id") Long id);
+	
+	// Named Query (using JPL) It is portable.
+	// See de.spring.persistence.example.domain.Ad
+	@Query("SELECT * FROM AD WHERE AD.ID = :id")
+	Ad findByIdQuery(@Param("id") Long id);
 }
diff --git a/SpringJava/JPA/src/main/java/de/spring/rest/controllers/AdController.java b/SpringJava/JPA/src/main/java/de/spring/rest/controllers/AdController.java
index 16a61f9..ae0e29f 100644
--- a/SpringJava/JPA/src/main/java/de/spring/rest/controllers/AdController.java
+++ b/SpringJava/JPA/src/main/java/de/spring/rest/controllers/AdController.java
@@ -1,5 +1,7 @@
 package de.spring.rest.controllers;
 
+import javax.inject.Inject;
+
 import org.resthub.web.controller.RepositoryBasedRestController;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -9,7 +11,13 @@ import de.spring.persistence.example.repository.AdRepository;
 
 @RestController
 @RequestMapping("/ads/")
-public class AdController extends RepositoryBasedRestController<Ad, Long, AdRepository>{
+public class AdController extends RepositoryBasedRestController<Ad, Long, AdRepository> {
 
+    @Override
+    @Inject
+    public void setRepository(AdRepository repository) {
+        this.repository = repository;
+    }
+    
 	// I do not have to do anything here because all I need is implemented by RepositoryBasedRestController :)
 }
diff --git a/SpringJava/JPA/src/main/resources/spring-configuration/datasource-configuration.xml b/SpringJava/JPA/src/main/resources/spring-configuration/datasource-configuration.xml
index 0951744..babff20 100644
--- a/SpringJava/JPA/src/main/resources/spring-configuration/datasource-configuration.xml
+++ b/SpringJava/JPA/src/main/resources/spring-configuration/datasource-configuration.xml
@@ -3,42 +3,27 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:util="http://www.springframework.org/schema/util"
        xmlns:tx="http://www.springframework.org/schema/tx"
+       xmlns:jee="http://www.springframework.org/schema/jee"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/util
         http://www.springframework.org/schema/util/spring-util.xsd
         http://www.springframework.org/schema/tx 
-        http://www.springframework.org/schema/tx/spring-tx.xsd">
+        http://www.springframework.org/schema/tx/spring-tx.xsd
+        http://www.springframework.org/schema/jee
+        http://www.springframework.org/schema/jee/spring-jee.xsd">
 
 	
 	<!-- enable the configuration of transactional behavior based on annotations -->
     <tx:annotation-driven transaction-manager="transactionManager"/>
 
- 	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
-        <property name="user" value="db2inst1"/>
-        <property name="password" value="db2inst1"/>
-        <property name="driverClass" value="com.ibm.db2.jcc.DB2Driver"/>
-        <!--
-	        If you are dropping like me (by means of some firewall) IPV6 connections and you feel
-	        during the first SLQ connection as if there is a huge lag and you are using
-	        *NIX, you could use this system property -Djava.net.preferIPv4Stack=true
-	        in order to stop using IPV6 from JVM.
-	        The JVM tries to find out if IPV6 is available by means of opening a random
-	        AF_INET6 POSIX socket.
-         -->
-        <property name="jdbcUrl" value="jdbc:db2://localhost:50000/velabd:allowNextOnExhaustedResultSet=1;currentSchema=CLOSURE;"/>
-        <property name="initialPoolSize" value="5"/>
-        <property name="maxPoolSize" value="20"/>
-        <property name="minPoolSize" value="10"/>
-        <property name="acquireIncrement" value="1"/>
-        <property name="acquireRetryAttempts" value="5"/>
-        <property name="acquireRetryDelay" value="1000"/>
-        <property name="automaticTestTable" value="con_test"/>
-        <property name="checkoutTimeout" value="5000"/>
-    </bean>
+
+	<!-- Using external provided datasource (in my case the one from Tomcat) -->
+	<jee:jndi-lookup id="dataSource" jndi-name="jdbc/example" expected-type="javax.sql.DataSource"/>
+    
     
    	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
-		<property name="entityManagerFactory" ref="jpaEntityManagerFactory" />
+		<property name="entityManagerFactory" ref="entityManagerFactory" />
 	</bean>
 	
 </beans>
diff --git a/SpringJava/JPA/src/main/resources/spring-configuration/jpa-configuration.xml b/SpringJava/JPA/src/main/resources/spring-configuration/jpa-configuration.xml
index 286d067..1a16615 100644
--- a/SpringJava/JPA/src/main/resources/spring-configuration/jpa-configuration.xml
+++ b/SpringJava/JPA/src/main/resources/spring-configuration/jpa-configuration.xml
@@ -19,7 +19,7 @@
     
     <context:property-placeholder location="classpath:jpa.properties" />
     
-	<bean id="jpaEntityManagerFactory"
+	<bean id="entityManagerFactory"
 		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
 		<property name="dataSource" ref="dataSource" />
 		<property name="jpaVendorAdapter">
@@ -31,7 +31,7 @@
 		<property name="packagesToScan" value="de.spring.persistence.**.domain" />
 	</bean>
 
-	<jpa:repositories entity-manager-factory-ref="jpaEntityManagerFactory"
+	<jpa:repositories entity-manager-factory-ref="entityManagerFactory"
 		base-package="de.spring.persistence.**.repository"
 		transaction-manager-ref="transactionManager" />
 
diff --git a/SpringJava/JPA/src/main/webapp/WEB-INF/web.xml b/SpringJava/JPA/src/main/webapp/WEB-INF/web.xml
index 01de2a8..31173b6 100644
--- a/SpringJava/JPA/src/main/webapp/WEB-INF/web.xml
+++ b/SpringJava/JPA/src/main/webapp/WEB-INF/web.xml
@@ -37,5 +37,25 @@
         <!-- REQUIRED PATTERN BY swagger-ui. IT DOESN'T WORK WITH ANY OTHER o.O -->
         <url-pattern>/*</url-pattern>
     </servlet-mapping>
+    
+    
+    <!--
+    	Se serializan objetos Entity al vuelo.
+    	
+    	1. JACKSON irá accediendo a todas las propiedades de un objeto Entity.
+    	2. Según vaya accediendo se irán haciendo queries a base de datos. NO ANTES porque se hace en
+    	modo lazy, lo que quiere decir que tenemos el objeto Entity pero sus datos (lo que se obtiene
+    	después de hacer las queries) están vacíos. Hasta que no se accede a una propiedad determinada
+    	del objeto Entity no se hará realmente la query en base de datos. 
+     -->
+    <filter>
+        <filter-name>OpenEntityManagerInViewFilter</filter-name>
+        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>OpenEntityManagerInViewFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+    
 
 </web-app>
-- 
2.1.4