More JPA stuff
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Fri, 1 Jul 2016 20:58:59 +0000 (22:58 +0200)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Fri, 1 Jul 2016 20:58:59 +0000 (22:58 +0200)
SpringJava/JPA/context.xml [new file with mode: 0644]
SpringJava/JPA/pom.xml
SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/Ad.java
SpringJava/JPA/src/main/java/de/spring/persistence/example/domain/AdDescription.java [new file with mode: 0644]
SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdDescriptionRepository.java [new file with mode: 0644]
SpringJava/JPA/src/main/java/de/spring/persistence/example/repository/AdRepository.java
SpringJava/JPA/src/main/java/de/spring/rest/controllers/AdController.java
SpringJava/JPA/src/main/resources/spring-configuration/datasource-configuration.xml
SpringJava/JPA/src/main/resources/spring-configuration/jpa-configuration.xml
SpringJava/JPA/src/main/webapp/WEB-INF/web.xml

diff --git a/SpringJava/JPA/context.xml b/SpringJava/JPA/context.xml
new file mode 100644 (file)
index 0000000..e053ee3
--- /dev/null
@@ -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>
index c8a763a..1c800b3 100644 (file)
@@ -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>
                                </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>
index e29ec68..6784538 100644 (file)
@@ -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 (file)
index 0000000..a3b7d16
--- /dev/null
@@ -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 (file)
index 0000000..120b2c8
--- /dev/null
@@ -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);
+}
index 8230c43..b625fed 100644 (file)
@@ -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);
 }
index 16a61f9..ae0e29f 100644 (file)
@@ -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 :)
 }
index 0951744..babff20 100644 (file)
@@ -3,42 +3,27 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
        xmlns:util="http://www.springframework.org/schema/util"\r
        xmlns:tx="http://www.springframework.org/schema/tx"\r
+       xmlns:jee="http://www.springframework.org/schema/jee"\r
        xsi:schemaLocation="http://www.springframework.org/schema/beans\r
         http://www.springframework.org/schema/beans/spring-beans.xsd\r
         http://www.springframework.org/schema/util\r
         http://www.springframework.org/schema/util/spring-util.xsd\r
         http://www.springframework.org/schema/tx \r
-        http://www.springframework.org/schema/tx/spring-tx.xsd">\r
+        http://www.springframework.org/schema/tx/spring-tx.xsd\r
+        http://www.springframework.org/schema/jee\r
+        http://www.springframework.org/schema/jee/spring-jee.xsd">\r
 \r
        \r
        <!-- enable the configuration of transactional behavior based on annotations -->\r
     <tx:annotation-driven transaction-manager="transactionManager"/>\r
 \r
-       <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">\r
-        <property name="user" value="db2inst1"/>\r
-        <property name="password" value="db2inst1"/>\r
-        <property name="driverClass" value="com.ibm.db2.jcc.DB2Driver"/>\r
-        <!--\r
-               If you are dropping like me (by means of some firewall) IPV6 connections and you feel\r
-               during the first SLQ connection as if there is a huge lag and you are using\r
-               *NIX, you could use this system property -Djava.net.preferIPv4Stack=true\r
-               in order to stop using IPV6 from JVM.\r
-               The JVM tries to find out if IPV6 is available by means of opening a random\r
-               AF_INET6 POSIX socket.\r
-         -->\r
-        <property name="jdbcUrl" value="jdbc:db2://localhost:50000/velabd:allowNextOnExhaustedResultSet=1;currentSchema=CLOSURE;"/>\r
-        <property name="initialPoolSize" value="5"/>\r
-        <property name="maxPoolSize" value="20"/>\r
-        <property name="minPoolSize" value="10"/>\r
-        <property name="acquireIncrement" value="1"/>\r
-        <property name="acquireRetryAttempts" value="5"/>\r
-        <property name="acquireRetryDelay" value="1000"/>\r
-        <property name="automaticTestTable" value="con_test"/>\r
-        <property name="checkoutTimeout" value="5000"/>\r
-    </bean>\r
+\r
+       <!-- Using external provided datasource (in my case the one from Tomcat) -->\r
+       <jee:jndi-lookup id="dataSource" jndi-name="jdbc/example" expected-type="javax.sql.DataSource"/>\r
+    \r
     \r
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">\r
-               <property name="entityManagerFactory" ref="jpaEntityManagerFactory" />\r
+               <property name="entityManagerFactory" ref="entityManagerFactory" />\r
        </bean>\r
        \r
 </beans>\r
index 286d067..1a16615 100644 (file)
@@ -19,7 +19,7 @@
     \r
     <context:property-placeholder location="classpath:jpa.properties" />\r
     \r
-       <bean id="jpaEntityManagerFactory"\r
+       <bean id="entityManagerFactory"\r
                class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">\r
                <property name="dataSource" ref="dataSource" />\r
                <property name="jpaVendorAdapter">\r
@@ -31,7 +31,7 @@
                <property name="packagesToScan" value="de.spring.persistence.**.domain" />\r
        </bean>\r
 \r
-       <jpa:repositories entity-manager-factory-ref="jpaEntityManagerFactory"\r
+       <jpa:repositories entity-manager-factory-ref="entityManagerFactory"\r
                base-package="de.spring.persistence.**.repository"\r
                transaction-manager-ref="transactionManager" />\r
 \r
index 01de2a8..31173b6 100644 (file)
         <!-- 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>