一、环境准备
springboot 1.5.4环境
数据库:spring的h2内存数据库
application.properties配置
server.port=8080
# 数据源配置
# 表明使用的数据库平台是h2
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:ssb_test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=root
spring.datasource.password=123456
h2数据库说明
h2数据库是一个基于内存的数据库,十分的轻量级,它提供了一个web控制界面。
具体的配置暂不做介绍,他的可视化界面如下:
操作很简单:
服务启动之后访问地址http://localhost:8080/h2/login.jsp
选择汉语,配置,密码什么的都照着配置文件抄就好了。
1选择数据库(点击左侧的Customer)
2.点击Run
这样就可以看出来他里面的数据
需要注意的,这个数据库声明周期和spring一样,容器关闭了他也关闭了,可以利用spring的声明周期,让测试数据在启动的时候就已经提供好。
pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-accessing-data-jpa</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
二、代码实现
建立一个实体pojo
package com.gysoft.springdatajpa.forall.pojo;
import javax.persistence.*;
@Entity
@Table(name = "Customer")
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}
这个pojo和数据库的字段一一对应,但是数据库默认的会生成大写加下划线命名的方式,为了使其和pojo里面的字段保持一致,可以在application.properties配置文件里面加入如下的配置进行声明
#解决数据库字段变成驼峰命名的问题,这样的话数据库字段和pojo字段就一样了
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
dao层接口的实现
package com.gysoft.springdatajpa.forall.dao;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/6/13 9:24
*/
import com.gysoft.springdatajpa.forall.pojo.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
/**
* 根据lastname查找
* @param lastName
* @return
*/
List<Customer> findByLastName(String lastName);
/**
* 多条件 根据lastName和firstname
* @author weiwensi
* @date 14:05 2019/6/13
*@param firstName
*@param lastName
* @return
* @throws Exception
* @version 2.1
**/
List<Customer> findCustomerByFirstNameAndAndLastName(String firstName,String lastName);
/**
* 根据id查询
* @author weiwensi
* @date 14:14 2019/6/13
*@param
* @return
* @throws Exception
* @version 2.1
**/
Customer findById(Long id);
/**
* 根据firstName查询
*
*/
Customer findByFirstName(String firstName);
/**
* 模糊匹配
* @param bauer
* @return
*/
@Query("select c from Customer c where c.firstName=?1")
List<Customer> findByFirstName2(String bauer);
/**
* 测试实体sql的查询
* @param lastName
* @return
*/
@Query(value = "select * from customer where lastName=? order by id desc",nativeQuery = true)
// @Query("select c from Customer c where c.lastName=?1 order by c.id desc")
List<Customer> findByLastName2(String lastName);
/**
* 一个参数,匹配两个字段
* @param name2
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
*/
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name order by c.id desc")
List<Customer> findByName(@Param("name") String name2);
/**
* 一个参数,匹配两个字段
* @param name
* @return
* 这里的%只能放在占位的前面,后面不行
*/
@Query("select c from Customer c where c.firstName like %?1")
List<Customer> findByName2(@Param("name") String name);
/**
* 一个参数,匹配两个字段
* @param name
* @return
* 开启nativeQuery=true,在value里可以用原生SQL语句完成查询
*/
@Query(nativeQuery = true,value = "select * from Customer c where c.first_name like concat('%' ,?1,'%') ")
List<Customer> findByName3(@Param("name") String name);
}
这个接口继承了JpaRepository,他的接口方法定义如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.data.jpa.repository;
import java.io.Serializable;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAll(Iterable<ID> var1);
<S extends T> List<S> save(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
从这个接口里面可以看出来这个接口里面定义一些基本的增删改查,springjpa我个人认为采用了约束大于配置的思想,基本的增删改查不用写sql很简单就可以实现
他提供了自己的语法配置,具体的配置如下:
再次拼接and条件
就是类似这样一步步拼接,实现起来很简单,简单的增删改查基本都满足了。
一些基本的测试如下:(注意每次启动项目都需要重新访问index对应得接口,这里没有整合spring进行优化,后面会进行优化,不必要每次都重新创建测试数据)
/**
* 初始化数据
*/
@RequestMapping("/index")
public void index() {
// save a couple of customers
//这里使用了代理的方式实现了数据的存储
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
repository.save(new Customer("Bauer", "Dessler"));
}
/**
* 查询所有
*/
@RequestMapping("/findAll")
@ResponseBody
public void findAll(){
Iterable<Customer> result = repository.findAll();
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
/**
* 查询ID为1的数据
*/
@RequestMapping("/findOne")
@ResponseBody
public Customer findOne(){
Customer result = repository.findOne(1L);
if(result!=null){
System.out.println(result.toString());
}
System.out.println("-------------------------------------------");
return result;
}
/**
* 查询ID为1的数据
*/
@RequestMapping("/delete")
public void delete(){
System.out.println("删除前数据:");
Iterable<Customer> customers = repository.findAll();
for (Customer customer:customers){
System.out.println(customer.toString());
}
System.out.println("删除ID=3数据:");
Customer cu = repository.findById(3L);
if(null==cu){
throw new IllegalArgumentException("您要删除的数据已经不存在");
}
repository.delete(3L);
System.out.println("删除后数据:");
customers = repository.findAll();
for (Customer customer:customers){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
}
/**
* 根据lastName查询
*/
@RequestMapping("/findByLastName")
@ResponseBody
public List<Customer> findByLastName(){
List<Customer> result = repository.findByLastName("Bauer");
for (Customer customer:result){
System.out.println(customer.toString());
}
System.out.println("-------------------------------------------");
List<Customer> customerByFirstNameAndAndLastName = repository.findCustomerByFirstNameAndAndLastName("Jack", "Bauer");
customerByFirstNameAndAndLastName.forEach(e->{
System.out.println("根据firstname和lastName查询的结果"+e);
});
return customerByFirstNameAndAndLastName;
}
springdatajpa 除了这些简单的增删改查,还可以通过注解进行一个sql的指定,如果你想写sql那就这么干吧
/**
* 模糊匹配
* @param bauer
* @return
*/
@Query("select c from Customer c where c.firstName=?1")
List<Customer> findByFirstName2(String bauer);
/**
* 测试实体sql的查询
* @param lastName
* @return
*/
@Query(value = "select * from customer where lastName=? order by id desc",nativeQuery = true)
// @Query("select c from Customer c where c.lastName=?1 order by c.id desc")
List<Customer> findByLastName2(String lastName);
/**
* 一个参数,匹配两个字段
* @param name2
* @return
* 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
*/
@Query("select c from Customer c where c.firstName=:name or c.lastName=:name order by c.id desc")
List<Customer> findByName(@Param("name") String name2);
/**
* 一个参数,匹配两个字段
* @param name
* @return
* 这里的%只能放在占位的前面,后面不行
*/
@Query("select c from Customer c where c.firstName like %?1")
List<Customer> findByName2(@Param("name") String name);
/**
* 一个参数,匹配两个字段
* @param name
* @return
* 开启nativeQuery=true,在value里可以用原生SQL语句完成查询
*/
@Query(nativeQuery = true,value = "select * from Customer c where c.first_name like concat('%' ,?1,'%') ")
List<Customer> findByName3(@Param("name") String name);
这里的sql大部分都是使用了JPQL的语言方式,如果你需要使用原生的sql,你可以通过注解的参数nativeQuery=true 进行。
切换譬如上面的
@Query(value = "select * from customer where lastName=? order by id desc",nativeQuery = true)
// @Query("select c from Customer c where c.lastName=?1 order by c.id desc")
List<Customer> findByLastName2(String lastName);
对于里面的一些问题稍微解释一下:
?加数字表示占位符,?1代表在方法参数里的第一个参数,区别于其他的index,这里从1开始
=:加上变量名,这里是与方法参数中有@Param的值匹配的,而不是与实际参数匹配的
JPQL的语法中,表名的位置对应Entity的名称,字段对应Entity的属性,详细语法见相关文档
官方文档地址:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
这样基本实现了大部分的简单增删改查。附一个我的application。properties的完整配置
server.port=8080
# 数据源配置
# 表明使用的数据库平台是h2
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:ssb_test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.schema=classpath:db/schema.sql
#spring.datasource.data=classpath:db/data.sql
# 进行该配置后,h2 web consloe就可以在远程访问了。否则只能在本机访问。
spring.h2.console.settings.web-allow-others=true
# 进行该配置,你就可以通过YOUR_URL/h2-console访问h2 web consloe。YOUR_URL是你程序的访问URl。
spring.h2.console.path=/h2
# 进行该配置,程序开启时就会启动h2 web consloe。当然这是默认的
spring.h2.console.enabled=true
#连接池配置
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#初始化连接:连接池启动时创建的初始化连接数量
spring.datasource.dbcp2.initial-size=50
#最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
spring.datasource.dbcp2.max-active=250
#最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
spring.datasource.dbcp2.max-idle=50
#最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
spring.datasource.dbcp2.min-idle=5
#最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待
spring.datasource.dbcp2.max-wait-millis=10000
#SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录
spring.datasource.dbcp2.validation-query=SELECT 1
#当建立新连接时被发送给JDBC驱动的连接参数,格式必须是 [propertyName=property;]。注意:参数user/password将被明确传递,所以不需要包括在这里。
spring.datasource.dbcp2.connection-properties=characterEncoding=utf8
# JPA配置
#hibernate提供了根据实体类自动维护数据库表结构的功能,可通过spring.jpa.hibernate.ddl-auto来配置,有下列可选项:
#1、create:启动时删除上一次生成的表,并根据实体类生成表,表中数据会被清空。
#2、create-drop:启动时根据实体类生成表,sessionFactory关闭时表会被删除。
#3、update:启动时会根据实体类生成表,当实体类属性变动的时候,表结构也会更新,在初期开发阶段使用此选项。
#4、validate:启动时验证实体类和数据表是否一致,在我们数据结构稳定时采用此选项。
#5、none:不采取任何措施。
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.show-sql用来设置hibernate操作的时候在控制台显示其真实的sql语句。
spring.jpa.show-sql=true
#让控制器输出的json字符串格式更美观。
spring.jackson.serialization.indent-output=true
#解决数据库字段变成驼峰命名的问题,这样的话数据库字段和pojo字段就一样了
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#日志配置
logging.level.com.xiaolyuh=debug
logging.level.org.springframework.web=debug
logging.level.org.springframework.transaction=debug
logging.level.org.apache.commons.dbcp2=debug
#开启对AOP的支持
spring.aop.auto=true
#设置代理模式 true(cglib) false(java JDK代理)
spring.aop.proxy-target-class=true
debug=false
后面继续学习SpringDataJPA的其他东西。
参考:http://www.spring4all.com/article/500
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
在此感谢我的同事万强的悉心指教。
个人代码github地址
https://github.com/wws11/SpingDataJpa