Introduction
Spring JPA is a quick method for providing CRUD functionality around an RDBMS.
Using Spring JPA, I can define an
List<Gngram> findByCollectionNameAndFocalTermAndFocalPos(Pageable pageable, String collectionName, String focalTerm, String focalPos);
The implementation is handled automagically by Spring.
I don't have to write a single line of SQL, JQL, or any query language. This allows us to get basic CRUD functionality around any relational table or view in a matter of minutes.
In this example, we'll be using this table:
This table holds a Google n-Gram. The format of each record is explained here. As each record is read from a file, the database is updated.
Outline
- How to configure a project
- This tutorial assumes the use of Maven: Installing Maven and Creating a Java Project in Maven are important skills to have.
- A background in Dependency Injection with Spring is essential.
- Java Package Layout
- Defining the Entity
- Defining the Repository
- Writing a test class
- Custom Methods
- When Spring JPA is not sufficient
Environment
- JDK 7
- Maven 3.2.3
- Spring
- Context, Core, JDBC 4.1.3
- Data-Commons and Data-JPA 4.1.4
- MySQL 5.1.27
The POM
My POM file looks like this: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>mycompany-app</artifactId>
<version>4.1.4</version>
<packaging>pom</packaging>
<properties>
<spring-context.version>4.1.3.RELEASE</spring-context.version>
<spring-core.version>4.1.3.RELEASE</spring-core.version>
<spring-jdbc.version>4.1.3.RELEASE</spring-jdbc.version>
<spring-data-commons-core.version>1.4.1.RELEASE</spring-data-commons-core.version>
<spring-data-jpa.version>1.7.1.RELEASE</spring-data-jpa.version>
<hibernate-entitymanager.version>4.3.7.Final</hibernate-entitymanager.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-context.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<!--version>1.6.3.RELEASE</version-->
<version>1.9.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.7.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.1.Final</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
</dependency>
<!-- Commons JDBC -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>20030825.183949</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>20030825.184428</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>20040616</version>
</dependency>
</dependencies>
</project>
The groupId and artifactId in red text above should be changed.
The Application Context
The primary configuration is in the application-context.xml file for the project. This configuration file provides the context for the Spring IoC container.My context file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:repository="http://www.springframework.org/schema/data/repository" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.6.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:annotation-config />
<jpa:repositories base-package="org.swtk.ngrams.google.db.data.repositories" />
<context:component-scan base-package="org.swtk.ngrams.google.db.data" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="datasource" />
<property name="persistenceUnitName" value="swtk-gngrams" />
<property name="packagesToScan" value="org.swtk.ngrams.google.db" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="database" value="MYSQL" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/gngrams" />
<property name="username" value="userid" />
<property name="password" value="password" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
</beans>
The text highlighted in red should be changed.
Package Layout
I use the following package layout:
- com.mycompany.app.data
- entities
- repositories
- service
The entities package contains the persistent POJOs that map to tables in the database. The repositories package contains granular methods for accessing data in the database. The service package contains coarser-grained methods for accessing data; perhaps using multiple methods from multiple repositories and returning a combined result to a consumer.
Entities
My entity class reflects the table in the image at the start of this article.To any developer already familiar with ORM solutions, the annotations on this POJO should be straightforward:
package org.swtk.ngrams.google.db.data.entities; 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 org.swtk.common.core.util.StringUtils; @Entity @Table(name = "gngram") public class Gngram {
/** * Purpose: * The name of the collection file * * eg. this is typically the underlying source file that contains the google n-grams */ @Column(name = "COLLECTION_NAME") private String collectionName;
/** * Purpose: * The term with an attached part-of-speech * * eg. given this n-Gram: * "had well_ADV organized" * the focal pos is * "ADV" */ @Column(name = "FOCAL_POS") private String focalPos;
/** * Purpose: * The term with an attached part-of-speech * * eg. given this n-Gram: * "had well_ADV organized" * the focal term is * "well" */ @Column(name = "FOCAL_TERM") private String focalTerm;
/** * Purpose: * The primary key */ @Id @Column(name = "ID") @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id;
/** * Purpose: * The number of times the n-Gram was found (across the entire collection) */ @Column(name = "MATCH_COUNT") private Double matchCount;
/** * Purpose: * The number of volumes the n-Gram was found in */ @Column(name = "VOLUME_COUNT") private Double volumeCount; public String getCollectionName() { return collectionName; } public String getFocalPos() { return focalPos; } public String getFocalTerm() { return focalTerm; } public Integer getId() { return id; } public Double getMatchCount() { return matchCount; } public Double getVolumeCount() { return volumeCount; } public boolean hasFocalPos() { return StringUtils.hasValue(getFocalPos()); } public boolean hasFocalTerm() { return StringUtils.hasValue(getFocalTerm()); } public void setCollectionName(String collectionName) { this.collectionName = collectionName; } public void setFocalPos(String focalPos) { this.focalPos = focalPos; } public void setFocalTerm(String focalTerm) { this.focalTerm = focalTerm; } public void setId(Integer id) { this.id = id; } public void setMatchCount(Double matchCount) { this.matchCount = matchCount; } public void setVolumeCount(Double volumeCount) { this.volumeCount = volumeCount; } }
Repositories
The Repository interface extends the JpaRepository interface, and takes the persistent entity as a generic type: package org.swtk.ngrams.google.db.data.repositories;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.swtk.ngrams.google.db.data.entities.Gngram;
@Repository
public interface GngramRepository extends JpaRepository<Gngram, Integer> {
List<Gngram> findByCollectionName(Pageable pageable, String collectionName);
List<Gngram> findByCollectionNameAndFocalPos(Pageable pageable, String collectionName, String focalPos);
List<Gngram> findByCollectionNameAndFocalTerm(Pageable pageable, String collectionName, String focalTerm);
List<Gngram> findByCollectionNameAndFocalTermAndFocalPos(Pageable pageable, String collectionName, String focalTerm, String focalPos);
}
The pageable parameter allows the specification of the page and number of results per page. The remaining parameters are inserted into the query generated by Spring for each of these method implementations.
Test Case
The test case looks like this:package org.swtk.ngrams.google.db.data.repositories; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.swtk.common.core.env.LogManager; import org.swtk.ngrams.google.db.data.entities.Gngram; import org.swtk.ngrams.google.db.util.adapter.GngramAdapter; import org.swtk.ngrams.ioc.NgramsIocContainer; public class GngramRepositoryTest { public static LogManager logger = new LogManager(GngramRepositoryTest.class); private static final String COLLECTION_NAME = "test-run-grt"; private void createAndSave(GngramRepository gngramRepository) throws Throwable { List<Gngram> list = new ArrayList<Gngram>(); list.add(GngramAdapter.transform(COLLECTION_NAME, "had well_ADV organized 1904 11 11")); list.add(GngramAdapter.transform(COLLECTION_NAME, "had well_NOUN organized 1934 22 22")); gngramRepository.save(list); } @Test public void run() throws Throwable { GngramRepository gngramRepository = NgramsIocContainer.getGngramsContext().getBean(GngramRepository.class); assertNotNull(gngramRepository);
/* clear database: test prep */
gngramRepository.delete(gngramRepository.findByCollectionName(null, COLLECTION_NAME));
/* create and save the entity */
createAndSave(gngramRepository);
/* test basic retrieval methods */
assertEquals(2, gngramRepository.findByCollectionNameAndFocalTerm(null, COLLECTION_NAME, "well").size()); assertEquals(1, gngramRepository.findByCollectionNameAndFocalPos(null, COLLECTION_NAME, "ADV").size());
/* clear database: test cleanup */
gngramRepository.delete(gngramRepository.findByCollectionName(null, COLLECTION_NAME)); } }
I don't have a services class defined for this example.
References
- Official Spring JPA Documentation
- At some point, every Spring JPA developer should read through this documentation completely.
- Supported Keywords Inside Method Names
- The complete list of keywords and samples.
- Essentially an EBNF-like table for writing methods in your interface.
- Using Custom Queries with Query Methods
- Generic queries can only take you so far.
- [Stackoverflow] Custom Methods belong in separate interfaces
- Putting a @Query annotation over a method in an interface that extends JpaRepository doesn't seem to work as well as some articles suggest.
mmorpg
ReplyDeleteinstagram takipçi satın al
tiktok jeton hilesi
tiktok jeton hilesi
antalya saç ekimi
ınstagram takipçi satın al
instagram takipçi satın al
MT2 PVP SERVERLAR
instagram takipçi satın al
perde modelleri
ReplyDeleteNumara onay
mobil ödeme bozdurma
Nft Nasıl Alınır
ankara evden eve nakliyat
TRAFİK SİGORTASI
Dedektor
Websitesi Kurma
AŞK ROMANLARI
smm panel
ReplyDeleteSmm Panel
İş ilanları blog
instagram takipçi satın al
hirdavatciburada.com
beyazesyateknikservisi.com.tr
servis
TİKTOK PARA HİLESİ İNDİR