--- /dev/null
+<?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&autoReconnect=true&characterEncoding=UTF-8"
+ username="root"
+ validationQuery="select 1" />
+
+
+</Context>
<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>
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.
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;
- }
}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+}
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);
}
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;
@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 :)
}
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
\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
<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
<!-- 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>