From: Gustavo Martin Morcuende Date: Fri, 23 Dec 2016 19:54:45 +0000 (+0100) Subject: spring-jpa-persistence, using gradle X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=03b7d21943d5ac1cbd4a5868ef5e19bbc5b366af;p=JavaForFun spring-jpa-persistence, using gradle --- diff --git a/SpringJava/Gradle/spring-jpa-persistence/build.gradle b/SpringJava/Gradle/spring-jpa-persistence/build.gradle new file mode 100644 index 0000000..172c202 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/build.gradle @@ -0,0 +1,118 @@ +group 'de.spring.jpa' +version '1.0-SNAPSHOT' + + +buildscript { + repositories { + mavenCentral() + maven { url 'https://plugins.gradle.org/m2/' } + } + dependencies { + classpath "gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.7" + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' +apply plugin: 'com.ewerk.gradle.plugins.querydsl' + + +targetCompatibility = 1.8 +sourceCompatibility = 1.8 + + +repositories { + mavenCentral() +} + +dependencies { + // 1/3 Required dependency for log4j 2 with slf4j: binding between log4j2 and slf4j + compile('org.apache.logging.log4j:log4j-slf4j-impl:2.7') + // 2/3 Required dependency for log4j 2 with slf4j: log4j 2 maven plugin (it is the log4j 2 implementation) + compile('org.apache.logging.log4j:log4j-core:2.7') + // 3/3 Required dependency for getting rid of commons logging. This is the BRIDGE (no binding) between Jakarta Commons Logging (used by Spring) + // and whatever I am using for logging (in this case I am using log4j 2) See: http://www.slf4j.org/legacy.html + // We need exclusions in every dependency using Jakarta Commons Logging (see Spring dependencies below) + compile('org.slf4j:jcl-over-slf4j:1.7.22') + + + compile('org.springframework:spring-context:4.3.5.RELEASE') { + exclude group: 'commons-logging', module: 'commons-logging' + } + compile('javax.inject:javax.inject:1') + compile('cglib:cglib:3.2.4') + + + // Required JPA dependencies with hibernate + compile('org.springframework:spring-orm:4.3.5.RELEASE') + compile('org.springframework.data:spring-data-jpa:1.10.5.RELEASE') + // Included dependency in spring-data-jpa + // compile('org.hibernate:hibernate-entitymanager:') + + + // Auditory using Hibernate Envers + compile('org.springframework.data:spring-data-envers:1.0.5.RELEASE') + // Included dependency in spring-data-envers + // compile('org.hibernate:hibernate-envers:5.2.5.Final') + + + // Required by spring-context for using JSR-303. See LocalValidatorFactoryBean + // in rest-config.xml + compile('javax.validation:validation-api:1.1.0.Final') + compile('org.hibernate:hibernate-validator:5.3.4.Final') + + + // Jackson JSON Processor, required by spring-webmvc. See messageConverters + // in rest-config.xml + compile('com.fasterxml.jackson.core:jackson-databind:2.8.5') + + + // Loading data base in run time + compile("org.liquibase:liquibase-core:3.5.3") + + + // Jackson Java time support + compile('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.4') + + + // Using Querydsl + // Included in spring-data-jpa + // compile('com.querydsl:querydsl-apt:4.1.3') + // compile('com.querydsl:querydsl-jpa:4.1.3') + + + // Unit tests + testCompile('junit:junit:4.12') + testCompile('org.mockito:mockito-core:2.4.1') + testCompile('org.springframework:spring-test:4.3.5.RELEASE') + + + // Integration tests + // Either using H2 + testCompile('com.h2database:h2:1.4.193') + // or MYSQL with docker :) + testCompile('mysql:mysql-connector-java:6.0.5') +} + + +test { + testLogging { + events "PASSED", "FAILED", "SKIPPED" + } +} + + +querydsl { + library = 'com.querydsl:querydsl-apt:4.1.3' + querydslSourcesDir = "${buildDir}/generated-sources/querydsl" + jpa = true +} + + +jar { + manifest { + attributes 'Implementation-Title': 'Spring JPA Persistence, gradle example', 'Implementation-Version': version + } +} + diff --git a/SpringJava/Gradle/spring-jpa-persistence/gradle.properties b/SpringJava/Gradle/spring-jpa-persistence/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/SpringJava/Gradle/spring-jpa-persistence/settings.gradle b/SpringJava/Gradle/spring-jpa-persistence/settings.gradle new file mode 100644 index 0000000..62c25f1 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring-jpa-persistence' diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/context/StaticContextHolder.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/context/StaticContextHolder.java new file mode 100644 index 0000000..b117739 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/context/StaticContextHolder.java @@ -0,0 +1,39 @@ +package de.spring.example.context; + +import javax.inject.Named; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; + +/** + * JPA Entity classes do not allow you to inject beans. + * I tried to use this class for injecting beans in them but it did not either work :( + * No way of injecting beans in JPA Entity classes :( + */ +@Named("staticContextHolder") +public class StaticContextHolder implements BeanFactoryAware { + private static final Logger LOGGER = LoggerFactory.getLogger(StaticContextHolder.class); + + private static BeanFactory CONTEXT; + + public StaticContextHolder() { + + } + + public static Object getBean(String bean) { + return CONTEXT.getBean(bean); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + if (CONTEXT != null) { + LOGGER.warn("CONTEXT is not null!!!"); + } + + CONTEXT = beanFactory; + } + +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/context/UsernameThreadContext.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/context/UsernameThreadContext.java new file mode 100644 index 0000000..19e3ec0 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/context/UsernameThreadContext.java @@ -0,0 +1,28 @@ +package de.spring.example.context; + +import org.springframework.util.Assert; + +/** + * I had to implement this class in a static way because JPA Entity objects do not allow you + * to inject beans. StaticContextHolder did not either work :( + * No way of injecting beans in JPA Entity classes :( + */ +public class UsernameThreadContext { + public static final String USERNAME_HEADER = "USERNAME"; + + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static final void setUsername(String username) { + Assert.notNull(username); + + contextHolder.set(username); + } + + public static final String getUsername() { + return contextHolder.get(); + } + + public static final void clearUsername() { + contextHolder.remove(); + } +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/converters/LocalDateTimeAttributeConverter.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/converters/LocalDateTimeAttributeConverter.java new file mode 100644 index 0000000..3dc89a8 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/converters/LocalDateTimeAttributeConverter.java @@ -0,0 +1,34 @@ +package de.spring.example.persistence.converters; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class LocalDateTimeAttributeConverter implements AttributeConverter { + + @Override + public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) { + Timestamp timestamp = null; + + if (localDateTime != null) { + timestamp = Timestamp.valueOf(localDateTime); + } + + return timestamp; + } + + @Override + public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) { + LocalDateTime localDateTime = null; + + if (sqlTimestamp != null) { + localDateTime = sqlTimestamp.toLocalDateTime(); + } + + return localDateTime; + } + +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/converters/OffsetDateTimeAttributeConverter.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/converters/OffsetDateTimeAttributeConverter.java new file mode 100644 index 0000000..30ab6bb --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/converters/OffsetDateTimeAttributeConverter.java @@ -0,0 +1,39 @@ +package de.spring.example.persistence.converters; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class OffsetDateTimeAttributeConverter implements AttributeConverter { + + @Override + public Timestamp convertToDatabaseColumn(OffsetDateTime offsetDateTime) { + Timestamp timestamp = null; + + if (offsetDateTime != null) { + timestamp = Timestamp.valueOf(offsetDateTime.toLocalDateTime()); + } + + return timestamp; + } + + @Override + public OffsetDateTime convertToEntityAttribute(Timestamp sqlTimestamp) { + OffsetDateTime offsetDateTime = null; + + if (sqlTimestamp != null) { + final LocalDateTime localDateTime = sqlTimestamp.toLocalDateTime(); + final ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault()); + offsetDateTime = zonedDateTime.toOffsetDateTime(); + } + + return offsetDateTime; + } + +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/Ad.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/Ad.java new file mode 100644 index 0000000..dbfb184 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/Ad.java @@ -0,0 +1,161 @@ +package de.spring.example.persistence.domain; + +import java.io.Serializable; +import java.time.OffsetDateTime; +import java.util.Set; + +import javax.persistence.CascadeType; +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.JoinColumn; +//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 org.hibernate.envers.AuditJoinTable; +import org.hibernate.envers.AuditMappedBy; +import org.hibernate.envers.Audited; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +import de.spring.example.persistence.converters.OffsetDateTimeAttributeConverter; + +@Entity +//@Audited(withModifiedFlag=true) +@Table(name="AD", schema="mybatis_example") +@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="jsonId") +// 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 { + + @Audited(withModifiedFlag=true) + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="ID", updatable=false, nullable=false) + private Long id; + + // NO WAY OF AUDITING THIS COLUMN :(. AM I MESSING SOMETHING UP WITH JPA AND RELATIONS BETWEEN TABLES? + @AuditJoinTable(name="AD_DESCRIPTION_AUDITED") + @AuditMappedBy(mappedBy="ad", positionMappedBy="id") + @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "AD_ID", nullable = false) + private Set adDescriptions; + + @Audited(withModifiedFlag=true) + @Max(60) + @Column(name="COMPANY_ID") + private Long companyId; + + @Audited(withModifiedFlag=true) + @Max(40) + @Column(name="COMPANY_CATEG_ID") + private Long companyCategId; + + @Audited(withModifiedFlag=true) + @Size(min=2, max=255) + @Column(name="AD_MOBILE_IMAGE") + private String adMobileImage; + + @Audited(withModifiedFlag=true) + @NotNull + @Convert(converter=OffsetDateTimeAttributeConverter.class) + @Column(name="CREATED_AT", nullable=false) + @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ssZ") + private OffsetDateTime createdAt; + + @Audited(withModifiedFlag=true) + @NotNull + @Convert(converter=OffsetDateTimeAttributeConverter.class) + @Column(name="UPDATED_AT", nullable = false) + @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ssZ") + private OffsetDateTime updatedAt; + + // It will be used by JPA when filling the property fields with data coming from data base. + protected Ad() { + + } + + // It will be used by my code (for example by Unit Tests) + public Ad(Long id, Set adDescriptions, Long companyId, Long companyCategId, String adMobileImage, + OffsetDateTime createdAt, OffsetDateTime updatedAt) { + this.id = id; + this.adDescriptions = adDescriptions; + this.companyId = companyId; + this.companyCategId = companyCategId; + this.adMobileImage = adMobileImage; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + /** + * WARNING: JPA REQUIRES GETTERS!!! + */ + + public Long getId() { + return id; + } + + public Set getAdDescriptions() { + return adDescriptions; + } + + public Long getCompanyId() { + return companyId; + } + + public Long getCompanyCategId() { + return companyCategId; + } + + public String getAdMobileImage() { + return adMobileImage; + } + + public OffsetDateTime getCreatedAt() { + return createdAt; + } + + public OffsetDateTime getUpdatedAt() { + return updatedAt; + } +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/AdDescription.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/AdDescription.java new file mode 100644 index 0000000..0b07605 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/AdDescription.java @@ -0,0 +1,112 @@ +package de.spring.example.persistence.domain; + +import java.io.Serializable; + +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.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.validation.constraints.Max; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.envers.Audited; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +@Entity +@Audited +@Table(name="AD_DESCRIPTION", schema="mybatis_example") +@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="jsonId") +public class AdDescription implements Serializable { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="ID", updatable=false, nullable=false) + private Long id; + + @ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL, optional=true) + @JoinColumn(name="AD_ID", nullable=false, updatable = false, insertable = false, 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.ad = ad; + this.languageId = languageId; + this.adName = adName; + this.adDescription = adDescription; + this.adMobileText = adMobileText; + this.adLink = adLink; + } + + /** + * WARNING: JPA REQUIRES GETTERS!!! + */ + + 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/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/MyCustomRevision.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/MyCustomRevision.java new file mode 100644 index 0000000..a119bca --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/MyCustomRevision.java @@ -0,0 +1,71 @@ +package de.spring.example.persistence.domain.audit; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +/** import org.hibernate.envers.DefaultRevisionEntity; **/ +import org.hibernate.envers.RevisionEntity; +import org.hibernate.envers.RevisionNumber; +import org.hibernate.envers.RevisionTimestamp; + +import com.fasterxml.jackson.annotation.JsonFormat; + +@Entity +@RevisionEntity(MyCustomRevisionListener.class) +@Table(name="CUSTOM_REVISION", schema="mybatis_example") +public class MyCustomRevision /** extends DefaultRevisionEntity **/ { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="ID", updatable=false, nullable=false) + @RevisionNumber + // BE CAREFUL!!!! spring-data-envers JUST WORKS (I couldn't make it work with anything else) WITH Integer. NOT WITH Long :( + private Integer id; + + @Column(name="REVISION_DATE") + @Temporal(TemporalType.TIMESTAMP) + @RevisionTimestamp + @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss") + private Date revisionDate; + + @Column(name="USERNAME") + private String username; + + // It will be used by JPA when filling the property fields with data coming from data base. + protected MyCustomRevision() { + + } + + // It will be used by my code (for example by Unit Tests) + public MyCustomRevision(String username) { + this.username = username; + } + + /** + * WARNING: JPA REQUIRES GETTERS!!! + */ + + public Integer getId() { + return id; + } + + public Date getRevisionDate() { + return revisionDate; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/MyCustomRevisionListener.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/MyCustomRevisionListener.java new file mode 100644 index 0000000..ff5f57e --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/MyCustomRevisionListener.java @@ -0,0 +1,33 @@ +package de.spring.example.persistence.domain.audit; + +import org.hibernate.envers.RevisionListener; + +import de.spring.example.context.UsernameThreadContext; + +public class MyCustomRevisionListener implements RevisionListener { + + // It will be used by Hibernate. + protected MyCustomRevisionListener() { + + } + + @Override + public void newRevision(Object revisionEntity) { + MyCustomRevision myCustomRevision = (MyCustomRevision) revisionEntity; + + final String username = getSafeUsername(); + myCustomRevision.setUsername(username); + + } + + private String getSafeUsername() { + String userName = UsernameThreadContext.getUsername(); + + if (userName == null) { + userName = "NO_USER"; + } + + return userName; + } + +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/package-info.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/package-info.java new file mode 100644 index 0000000..137b183 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/audit/package-info.java @@ -0,0 +1,6 @@ +@QueryEntities({DefaultRevisionEntity.class}) +package de.spring.example.persistence.domain.audit; + + +import org.hibernate.envers.DefaultRevisionEntity; +import com.querydsl.core.annotations.QueryEntities; \ No newline at end of file diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/specifications/AdSpectifications.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/specifications/AdSpectifications.java new file mode 100644 index 0000000..2cadb54 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/domain/specifications/AdSpectifications.java @@ -0,0 +1,62 @@ +package de.spring.example.persistence.domain.specifications; + +import java.time.LocalDate; + +//import javax.persistence.criteria.CriteriaBuilder; +//import javax.persistence.criteria.CriteriaQuery; +//import javax.persistence.criteria.Predicate; +//import javax.persistence.criteria.Root; + +import org.springframework.data.jpa.domain.Specification; + +import de.spring.example.persistence.domain.Ad; + +public class AdSpectifications { + +// public static Specification createdToday() { +// return new Specification() { +// +// @Override +// public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { +// final LocalDate date = LocalDate.now(); +// +// return cb.equal(root.get("createdAt"), date); +// } +// +// }; +// +// } + + /** + * The same using lambda expressions + */ + public static Specification createdToday() { + return (root, query, cb) -> { + final LocalDate date = LocalDate.now(); + + return cb.equal(root.get("createdAt"), date); + }; + } + +// public static Specification mobileImage(String image) { +// return new Specification() { +// +// @Override +// public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { +// +// return cb.equal(root.get("adMobileImage"), image); +// } +// +// }; +// +// } + + /** + * The same using lambda expressions + */ + public static Specification mobileImage(String image) { + return (root, query, cb) -> { + return cb.equal(root.get("adMobileImage"), image); + }; + } +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/repository/AdDescriptionRepository.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/repository/AdDescriptionRepository.java new file mode 100644 index 0000000..e865253 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/repository/AdDescriptionRepository.java @@ -0,0 +1,25 @@ +package de.spring.example.persistence.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.history.RevisionRepository; + +import de.spring.example.persistence.domain.Ad; +import de.spring.example.persistence.domain.AdDescription; + +/** + * By default org.springframework.data.jpa.repository.support.SimpleJpaRepository + * will be the implementation for this interface. + * + * Be careful with @Transactional. SimpleJpaRepository has annotated methods. + * + */ +public interface AdDescriptionRepository extends + PagingAndSortingRepository, + /** https://github.com/spring-projects/spring-data-envers/pull/45 QueryDslPredicateExecutor, **/ + RevisionRepository { + + // Custom Query method (useful when the offered methods by PagingAndSortingRepository are not enough) + Page findByAd(Ad ad, Pageable pageable); +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/repository/AdRepository.java b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/repository/AdRepository.java new file mode 100644 index 0000000..9e99ee3 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/java/de/spring/example/persistence/repository/AdRepository.java @@ -0,0 +1,32 @@ +package de.spring.example.persistence.repository; + +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.history.RevisionRepository; +import org.springframework.data.repository.query.Param; + +import de.spring.example.persistence.domain.Ad; + +/** + * By default org.springframework.data.jpa.repository.support.SimpleJpaRepository + * will be the implementation for this interface. + * + * Be careful with @Transactional. SimpleJpaRepository has annotated methods. + * + */ +public interface AdRepository extends + PagingAndSortingRepository, + JpaSpecificationExecutor, + RevisionRepository { + + // 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 a from Ad a where a.id = :id") + Ad findByIdQuery(@Param("id") Long id); +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/jpa.properties b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/jpa.properties new file mode 100644 index 0000000..9f628d3 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/jpa.properties @@ -0,0 +1,2 @@ +jpa.dialect=org.hibernate.dialect.MySQL5Dialect +jpa.show_sql=false \ No newline at end of file diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/configuration.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/configuration.xml new file mode 100644 index 0000000..5e4c00d --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/configuration.xml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/datasource-configuration.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/datasource-configuration.xml new file mode 100644 index 0000000..6b5500c --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/datasource-configuration.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/jpa-configuration.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/jpa-configuration.xml new file mode 100644 index 0000000..bcdd8a3 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/jpa-configuration.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + _AUDITED + REVISION + REVISION_TYPE + true + org.hibernate.envers.strategy.ValidityAuditStrategy + REVISION_END + true + REVISION_END_TIMESTAMP + _MODIFIED + + + + + + + diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/changeLogs.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/changeLogs.xml new file mode 100644 index 0000000..b1af5e1 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/changeLogs.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/ddlChangelog.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/ddlChangelog.xml new file mode 100644 index 0000000..e972618 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/ddlChangelog.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/dmlChangelog.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/dmlChangelog.xml new file mode 100644 index 0000000..8d10e3a --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/dmlChangelog.xml @@ -0,0 +1,1960 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/liquibase.xml b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/liquibase.xml new file mode 100644 index 0000000..705fc46 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/main/resources/spring-configuration/liquibase/liquibase.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/test/java/de/spring/example/persitence/domain/AdTest.java b/SpringJava/Gradle/spring-jpa-persistence/src/test/java/de/spring/example/persitence/domain/AdTest.java new file mode 100644 index 0000000..eae39d3 --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/test/java/de/spring/example/persitence/domain/AdTest.java @@ -0,0 +1,62 @@ +package de.spring.example.persitence.domain; + +import static org.junit.Assert.assertEquals; + +import java.time.OffsetDateTime; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import de.spring.example.persistence.domain.Ad; +import de.spring.example.persistence.domain.AdDescription; + +public class AdTest { + // Ad + public static final Long AD_ID = 66L; + public static final Long COMPANY_ID = 2L; + public static final Long COMPANY_CATEG_ID = 3L; + public static final String AD_MOBILE_IMAGE = "slippers.jpg"; + public static final OffsetDateTime CREATED_AT = OffsetDateTime.now().minusDays(1); + public static final OffsetDateTime UPDATED_AT = OffsetDateTime.now(); + + // AdDescription + public static final Long AD_DESCRIPTION_ID = 99L; + public static final Long LANGUAGE_ID = 3L; + public static final String AD_NAME = "Slippers"; + public static final String AD_DESCRIPTION = "Slippers"; + public static final String AD_MOBILE_TEXT = "Buy it now!"; + public static final String AD_LINK = "http://gumartinm.name"; + public static final Set AD_DESCRIPTIONS = createAdDescriptions(); + + + @Test + public void whenCallingConstructorWithParametersThenCreateObject() { + Ad ad = createAd(); + + assertEquals(ad.getAdDescriptions(), AD_DESCRIPTIONS); + assertEquals(ad.getAdMobileImage(), AD_MOBILE_IMAGE); + assertEquals(ad.getCompanyCategId(), COMPANY_CATEG_ID); + assertEquals(ad.getCreatedAt(), CREATED_AT); + assertEquals(ad.getUpdatedAt(), UPDATED_AT); + assertEquals(ad.getId(), AD_ID); + } + + private static final Ad createAd() { + return new Ad(AD_ID, AD_DESCRIPTIONS, COMPANY_ID, COMPANY_CATEG_ID, AD_MOBILE_IMAGE, + CREATED_AT, UPDATED_AT); + } + + private static final AdDescription createAdDescription() { + return new AdDescription(AD_DESCRIPTION_ID, null, LANGUAGE_ID, AD_NAME, AD_DESCRIPTION, + AD_MOBILE_TEXT, AD_LINK); + } + + private static final Set createAdDescriptions() { + AdDescription adDescription = createAdDescription(); + Set adDescriptions = new HashSet<>(); + adDescriptions.add(adDescription); + + return adDescriptions; + } +} diff --git a/SpringJava/Gradle/spring-jpa-persistence/src/test/resources/datasource-test-configuration.xml b/SpringJava/Gradle/spring-jpa-persistence/src/test/resources/datasource-test-configuration.xml new file mode 100644 index 0000000..68cd61f --- /dev/null +++ b/SpringJava/Gradle/spring-jpa-persistence/src/test/resources/datasource-test-configuration.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + +