工程结构
需求:
现实现一个自定义模糊查询,对任意实体对象进行查询,值为字符串类型时就模糊查询,其他类型等于查询。
1.安装docker
安装步骤:略。
本例安装在windows环境下。
2.下载oracle-xe镜像
命令:
docker pull wnameless/oracle-xe-11g
下载后进行查看
命令:
docker images
3.启动oracle-xe镜像
docker run -d -p 9090:8080 -p 1521:1521 wnameless/oracle-xe-11g
将oracle-xe管理界面8080接口映射到本机的9090接口,将oracle-xe1521接口映射为本机1521接口。
打开virtualBox,设置-->网络-->高级-->端口转发,进行如下配置:
启动后,在浏览器输入http://localhost:9090/apex应该会看到如下界面。
4.新建springboot项目
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.wangh</groupId>
<artifactId>springdatajpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springdatajpa</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- oracle驱动 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- 包含大量Java常用工具类 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>
spring-boot-starter-data-elasticsearch
</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.安装oraclejdbc至maven仓库
因为maven仓库无oracle驱动,需自行打包驱动至本地仓库。详细操作见本博客的打包oracle驱动至maven仓库一节。
6.新建data.sql
新建data.sql放置在src/main/resources,
insert into person (id,name,age,address) values(hibernate_sequence.nextval,'王椭圆1',27,'西安');
insert into person (id,name,age,address) values(hibernate_sequence.nextval,'椭圆2',27,'北京');
insert into person (id,name,age,address) values(hibernate_sequence.nextval,'王椭圆3',27,'上海');
insert into person (id,name,age,address) values(hibernate_sequence.nextval,'王椭圆4',27,'广州');
insert into person (id,name,age,address) values(hibernate_sequence.nextval,'王了',27,'中国');
7.配置application.properties
配置数据源及jpa相关属性。
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:xe
spring.datasource.username=system
spring.datasource.password=oracle
#create:启动时会删除上一次生成表
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
#3 let output json more beautiful
spring.jackson.serialization.indent_output=true
8.定义映射实体
package com.wangh.springdatajpa.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
private String address;
public Person(Long id, String name, Integer age, String address) {
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Person(){
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
9.定义specification: CustomSpecs.java
package com.wangh.springdatajpa.specs;
import static com.google.common.collect.Iterables.toArray;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
public class CustomSpecs {
/**
* 对于任意实体对象进行查询,对象里有几个值查几个,当为字符串类型时模糊匹配,其他类型进行等于查询
* @param entityManager
* @param example
* @return
*/
@SuppressWarnings("unchecked")
public static<T> Specification<T> byAuto(final EntityManager entityManager, final T example){
//获得当前实体类对象类型
final Class<T> type = (Class<T>) example.getClass();
return new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//新建Predicate列表存储构造的查询条件
List<Predicate> predicates = new ArrayList<Predicate>();
//获得实体类的EntityType,可以从EntityType获得实体类的属性
EntityType<T> entity = entityManager.getMetamodel().entity(type);
//对实体类的所有属性循环
for(Attribute<T, ?> attr : entity.getDeclaredAttributes()){
Object attrValue = getValue(example, attr);
if(attrValue != null){
//当前属性值类型为字符串时
if(attr.getJavaType() == String.class){
//当前字符串不为空时
if(! org.springframework.util.StringUtils.isEmpty(attrValue)){
predicates.add(cb.like(root.get(attribute(entity, attr.getName(), String.class)), pattern((String)attrValue)));
}
}else {
predicates.add(cb.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())), attrValue));
}
}
}
return predicates.isEmpty() ? cb.conjunction() : cb.and(toArray(predicates, Predicate.class));
}
/**
* 获得实体类对象某一属性的值
* @param example
* @param attr
* @return
*/
private <T> Object getValue(T example, Attribute<T, ?> attr) {
return ReflectionUtils.getField((Field)attr.getJavaMember(), example);
}
/**
* 实体类的单独属性
* @param entity
* @param filedName
* @param filedClass
* @return
*/
private <E, T>SingularAttribute<T, E> attribute(EntityType<T> entity, String filedName, Class<E> filedClass) {
return entity.getDeclaredSingularAttribute(filedName, filedClass);
}
};
}
private static String pattern(String str) {
return "%" + str + "%";
}
}
10.定义接口 CustomRepository
package com.wangh.springdatajpa.specs;
import java.io.Serializable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
Page<T> findByAuto(T example, Pageable pageable);
}
11.定义实现
package com.wangh.springdatajpa.specs;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements CustomRepository<T, ID>{
private final EntityManager entityManager;
public CustomRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
@Override
public Page<T> findByAuto(T example, Pageable pageable) {
return findAll(CustomSpecs.byAuto(entityManager, example), pageable);
}
}
12.定义RepositoryFactoryBean
package com.wangh.springdatajpa.specs;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {
public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomRepositoryFactory(entityManager);
}
private static class CustomRepositoryFactory extends JpaRepositoryFactory{
public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
}
@SuppressWarnings("unchecked")
@Override
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
RepositoryInformation information, EntityManager entityManager) {
return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CustomRepositoryImpl.class;
}
}
}
13.使用
package com.wangh.springdatajpa.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import com.wangh.springdatajpa.model.Person;
import com.wangh.springdatajpa.specs.CustomRepository;
public interface PersonRepository extends CustomRepository<Person, Long>{
}
package com.wangh.springdatajpa.controller;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wangh.springdatajpa.dao.PersonRepository;
import com.wangh.springdatajpa.model.Person;
@RestController
public class PersonController {
@Resource
private PersonRepository personRepository;
@RequestMapping("/auto")
public Page<Person> auto(Person person){
Page<Person> ps = personRepository.findByAuto(person, new PageRequest(0, 10));
return ps;
}
}
14.配置
package com.wangh.springdatajpa;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.wangh.springdatajpa.dao.PersonRepository;
import com.wangh.springdatajpa.specs.CustomRepositoryFactoryBean;
@SpringBootApplication
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class SpringdatajpaApplication {
@Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(SpringdatajpaApplication.class, args);
}
}
15.运行
浏览器输入 localhost:8080/auto,查询全部。
浏览器输入 localhost:8080/auto?address=西,模糊查询。