<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 添加数据库连接池 druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
主键生成策略
-
@GeneratedValue(strategy=GenerationType.xxx)
指定主键的生成策略
-
IDENTITY
:根据数据库的主键自增长策略
-
GenerationType.TABLE
:使用一个特定的数据库表格来保存主键
-
GenerationType.SEQUENCE
:在某些数据库中,不支持主键自增长,比如Oracle,其提供了一种叫做”序列(sequence)”的机制生成主键。此时,GenerationType.SEQUENCE就可以作为主键生成策略。该策略的不足之处正好与TABLE相反,由于只有部分数据库(Oracle,PostgreSQL,DB2)支持序列对象,所以该策略一般不应用于其他数据库。类似的,该策略一般与另外一个注解一起使用@SequenceGenerator,@SequenceGenerator注解指定了生成主键的序列.然后JPA会根据注解内容创建一个序列(或使用一个现有的序列)。如果不指定序列,则会自动生成一个序列SEQ_GEN_SEQUENCE
-
GenerationType.AUTO
:把主键生成策略交给持久化引擎(persistence engine),持久化引擎会根据数据库在以上三种主键生成策略中选择其中一种。此种主键生成策略比较常用,由于JPA默认的生成策略就是GenerationType.AUTO,所以使用此种策略时.可以显式的指定@GeneratedValue(strategy = GenerationType.AUTO)也可以直接@GeneratedValue
- 实例如下:
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) //数据库自增
private Integer id;
配置
spring:
datasource: ## 配置数据源
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://118.31.15.108:3306/jpa?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
username: root
password: ****
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 6000
timeBetweenEvictionRunsMillis: 6000
minEvictableIdleTimeMillis: 25200000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
validationQuery: SELECT 1 FROM DUAL
RemoveAbandanded: true
removeAbandonedTimeout: 1800
logAbandoned: true
jpa:
show-sql: true #控制台打印sql语句
database: MYSQL # 指定数据库的类型,不填会默认检测
generate-ddl: false ## 是否自动生成表,默认是false
# hibernate:
# ddl-auto: update
创建一个实体类
/**
* 用户的实体类,其中的变量和数据库默认是以驼峰形式对应的,比如industryId,那么在表中的字段一定要是industry_id,否则将会报错
*/
@Table(name="t_user") //指定对应数据库对应的表名
@Entity //标记这是一个实体类
@Data //lombook的自动生成set,get
public class User{
@Id //标记主键
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer age;
private String address;
private Integer industryId; //在数据库中的对应字段一定要是industry_id
}
基本的查询
-
定义一个
UserRepository
,相当于Mybatis中的Mapper,如下:
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import cn.tedu.jpa.domain.User;
import java.lang.String;
/**
* JpaRepository的接口,相当于mapper
* 泛型:JpaRepository<User, Integer> :第一个是实体类的类型,第二个是主键的类型
*/
public interface UserRepositoryextends JpaRepository<User,Integer>{
/**
* 根据指定条件查询 Byxxx(where xxx=xxx),除了根据主键查询,否则返回的都是List
* 其中查询的条件对应的类型必须相同
*/
List<User>findByName(String name);
/**
* 并列条件查询 ,相当于where name=xxx and age=xxx
*/
List<User>findByNameAndAge(String name,Integer age);
/*
* 三个条件的并列查询 where xxx and xxx and xxx
*/
List<User>findByNameAndAgeAndIndustryId(String name,Integer age,Integer industryId);
/*
* Top或者First指定返回结果数量,默认返回第一个,相当于limit 1
*/
UserfindTopByNameAndAge(String name,Integer age);
/*
* Topn或者Firstn指定返回结果数量,这里的n表示返回的数量,相当于limit n
*/
List<User>findTop2ByNameAndAge(String name,Integer age);
/*
* In 相当于where age in(....) 其中的变量类型可以数组、List、Set只要是Collection即可,泛型必须和查询条件的类型一致
*/
List<User>findByAgeIn(Integer[] ages);
/*
* 统计数据 相当于select count(*) where age=xxx
*/
LongcountByAge(Integer age);
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaServerApplicationTests{
@Resource
private UserRepository userRepository;
/**
* 新增数据(单个)
*/
@Test
public void addUser(){
User user=new User();
user.setAge(22);
user.setName("陈加兵");
User user2 = userRepository.save(user); //自增主键的返回
System.out.println(user);
}
/**
* 批量插入
*/
@Test
public void saveAll(){
User user1=new User();
user1.setAge(22);
user1.setName("陈加兵");
User user2=new User();
user2.setAge(22);
user2.setName("陈加兵");
List<User> users=new ArrayList<>();
users.add(user1);
users.add(user2);
List<User> usersResturn=userRepository.saveAll(users); //返回列表,自增主键的返回
for (User user : usersResturn) {
System.out.println(user);
}
}
/**
* 更新数据,使用的仍然是save方法,如果其中包含id,那么就是更新数据,否则就是添加
* 1、如果有数据不更新,那么就出入null即可
*/
@Test
public void update(){
User user1=new User();
user1.setId(1);
user1.setAge(22);
user1.setName("郑元梅");
User user = userRepository.save(user1);
System.out.println(user);
}
/**
* 统计数据
*/
@Test
public void count(){
System.out.println(userRepository.count());
}
/**
* 统计数据
*/
@Test
public void countByAge(){
System.out.println(userRepository.countByAge(23));
}
@Test
public void findByName(){
List<User> users=userRepository.findTop2ByNameAndAge("陈加兵", 22);
for (User user : users) {
System.out.println(user);
}
}
@Test
public void findByAgeIn(){
Integer[] ages= {22,23};
List<User> users=userRepository.findByAgeIn(ages);
for (User user : users) {
System.out.println(user);
}
}
}
}
自定义查询@Query
使用HQL语句查询
/**
* 使用hql表达式查询,其中?1表示对应第一个参数,不能直接使用?作为占位符
*/
@Query(value="select u from User u where u.age=?1 and u.name=?2")
List<User>findUserList(Integer age,String name);
使用sql语句查询
/**
* 使用sql语句查询,其中nativeQuery表示使用本地查询,即是sql语句查询
*/
@Query(value="select * from t_user where age=?1 order by industry_id desc",nativeQuery=true)
List<User>findUserListByAge(Integer age);
删除和修改
-
使用自定义sql的时候,如果涉及到删除和修改的sql需要满足两个条件才能执行,如下:
@Modifying
/**
* 删除和修改信息,必须同时使用@Modifying注解标注
*/
@Modifying
@Query(value="delete from t_user where industry_id=?1",nativeQuery=true)
void deleteByIndustryId(Integer industryId);
复杂条件查询
-
Repository接口需要继承
JpaSpecificationExecutor
,如下:
public interface UserRepositoryextends JpaRepository<User,Integer>,JpaSpecificationExecutor<User>{
/**
* 结果筛选
* @param user 封装了查询的条件
* @return
*/
public List<User> findAll_2(User user){
List<User> users = userRepository.findAll(new Specification<User>() {
/**
* @param root 根对象,用于封装查询的条件,比如name,jack、age 10等
* @param query 封装查询关键字 比如group by order by
* @param criteriaBuilder 封装对象条件
* @return 返回null表示没有查询条件
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder){
//new 一个集合,存放所有的查询条件
List<Predicate> predicates=new ArrayList<>();
if (!StringUtils.isEmpty(user.getName())) { //如果name不是null,就填入到筛选条件中
//第一个参数是表达式,第二个参数是值,相当于where name=%name%
Predicate predicate = criteriaBuilder.like(root.get("name").as(String.class),"%"+user.getName()+"%");
predicates.add(predicate); //加入到条件集合中
}
if (!StringUtils.isEmpty(user.getAddress())) { //如果地址不为空,填入筛选条件
//where address=xxx
Predicate predicate = criteriaBuilder.equal(root.get("address").as(String.class), user.getAddress());
predicates.add(predicate);
}
if (user.getAge()!=null) { //如果年龄不为空
//where age<=xxx
Predicate predicate = criteriaBuilder.le(root.get("age").as(Integer.class), user.getAge());
predicates.add(predicate);
}
Predicate[] parray=new Predicate[predicates.size()];
//返回,这里的使用的and条件,将上面所有的条件用and连接
return criteriaBuilder.and(predicates.toArray(parray));
}
});
return users;
}
/**
* 测试
*/
@Test
public void test1(){
User user=new User();
user.setName("a"); //封装name
List<User> users = findAll_2(user);
for (User user2 : users) {
System.out.println(user2);
}
}
分页查询
PageRequest
-
构造方法如下:
-
public PageRequest(int page, int size)
size
page
-
public PageRequest(int page, int size, Direction direction, String... properties)
direction
properties
Page
int getTotalPages()
long getTotalElements()
boolean hasContent();
List<T> getContent();
boolean isFirst();
boolean isLast();
boolean hasNext();
boolean hasPrevious();
Pageable nextPageable();
Pageable previousPageable();
简单查询
@Test
public void findAll(){
//构造分页数据,查找第二页,每页2条记录,order by age,industryId desc
Pageable pageable=new PageRequest(1, 2,Direction.DESC,"age","industryId");
Page<User> pages = userRepository.findAll(pageable); //执行分页查询的方法
if (pages.hasContent()) { //如果查询到了内容
List<User> users = pages.getContent(); //获取查询到的结果
long total = pages.getTotalElements(); //获取总数
}
}
@Test
public void findAll(){
Order order1=new Order(Direction.DESC, "age"); //创建排序方式
Order order2=new Order(Direction.ASC, "industryId");
List<Order> orders=new ArrayList<>(); //放入集合
orders.add(order1);
orders.add(order2);
Sort sort=new Sort(orders); //创建Sort
//构造分页数据,查找第二页,每页2条记录,order by age desc,industryId asc
Pageable pageable=new PageRequest(0, 2,sort);
Page<User> pages = userRepository.findAll(pageable); //执行分页查询的方法
if (pages.hasContent()) { //如果查询到了内容
List<User> users = pages.getContent(); //获取查询到的结果
long total = pages.getTotalElements(); //获取总数
for (User user : users) {
System.out.println(user);
}
}
}
复杂条件分页查询
-
Repository接口需要继承
JpaSpecificationExecutor
,如下:
public interface UserRepositoryextends JpaRepository<User,Integer>,JpaSpecificationExecutor<User>{
/**
* 结果筛选
* @param user 封装了查询的条件
* @return
*/
public List<User> findAll_3(User user,Integer pageNum,Integer pageSize){
Pageable pageable=new PageRequest(pageNum-1, pageSize); //分页的查询
Page<User> pages = userRepository.findAll(new Specification<User>() {
/**
* @param root 根对象,用于封装查询的条件,比如name,jack、age 10等
* @param query 封装查询关键字 比如group by order by
* @param criteriaBuilder 封装对象条件
* @return 返回null表示没有查询条件
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder){
//new 一个集合,存放所有的查询条件
List<Predicate> predicates=new ArrayList<>();
if (!StringUtils.isEmpty(user.getName())) { //如果name不是null,就填入到筛选条件中
//第一个参数是表达式,第二个参数是值,相当于where name=%name%
Predicate predicate = criteriaBuilder.like(root.get("name").as(String.class),"%"+user.getName()+"%");
predicates.add(predicate); //加入到条件集合中
}
if (!StringUtils.isEmpty(user.getAddress())) { //如果地址不为空,填入筛选条件
//where address=xxx
Predicate predicate = criteriaBuilder.equal(root.get("address").as(String.class), user.getAddress());
predicates.add(predicate);
}
if (user.getAge()!=null) { //如果年龄不为空
//where age<=xxx
Predicate predicate = criteriaBuilder.le(root.get("age").as(Integer.class), user.getAge());
predicates.add(predicate);
}
Predicate[] parray=new Predicate[predicates.size()];
//返回,这里的使用的and条件,将上面所有的条件用and连接
return criteriaBuilder.and(predicates.toArray(parray));
}
},pageable);
return pages.getContent(); //返回结果
}
/**
* 测试
*/
@Test
public void test2(){
User user=new User();
user.setName("a"); //封装name
List<User> users = findAll_3(user,1,10);
for (User user2 : users) {
System.out.println(user2);
}
}
https://chenjiabing666.github.io/2018/12/20/SpringBoot整合Spring-data-jpa/