在上一篇博客SpringBoot 入门(三)——数据持久化中,已经明白了如何利用注解为实体类创建对应的表以及表与表之间的关系,那么有了表过后就要对表进行操作,SpringBoot 自带的 JPA 对于新手来说简直就是神器,只需要定义一个 Repository 接口,基本不用写什么代码,一些常规的 CRUD 操作就有了,下面就看看这是如何操作的。
引入的依赖 JPA 部分跟上一篇博客一样,就不重复记录了,因为还会进行单元测试,所以还需要单元测试的相关依赖:
<!-- 单元测试所需要的依赖 start -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!-- 单元测试所需要的依赖 end -->
一 定义实体类
为了简单,这里新定义一个用来测试的计算机实体类,没有复杂的表之间的关联关系。
package com.qinshou.springbootdemo.bean;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* Description:计算机实体类
* Author: QinHao
* Date: 2019/7/31 11:33
*/
@Entity
@Table(name = "computer")
public class ComputerBean {
/**
* 自增长 Id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
/**
* 品牌
*/
@Column(name = "brand")
private String brand;
/**
* CPU
*/
@Column(name = "cpu")
private String cpu;
/**
* 核心数
*/
@Column(name = "core")
private Integer core;
/**
* 主板
*/
@Column(name = "main_board")
private String mainBoard;
/**
* 内存
*/
@Column(name = "memory")
private Integer memory;
/**
* 硬盘
*/
@Column(name = "hard_disk")
private Integer hardDisk;
/**
* 硬盘
*/
@Column(name = "power")
private String power;
/**
* 屏幕尺寸
*/
@Column(name = "screen_inch")
private Float screenInch;
public ComputerBean() {
}
public ComputerBean(String brand, String cpu, Integer core, String mainBoard, Integer memory, Integer hardDisk, String power, Float screenInch) {
this.brand = brand;
this.cpu = cpu;
this.core = core;
this.mainBoard = mainBoard;
this.memory = memory;
this.hardDisk = hardDisk;
this.power = power;
this.screenInch = screenInch;
}
@Override
public String toString() {
return "ComputerBean{" +
"id=" + id +
", brand='" + brand + '\'' +
", cpu='" + cpu + '\'' +
", core=" + core +
", mainBoard='" + mainBoard + '\'' +
", memory=" + memory +
", hardDisk=" + hardDisk +
", power='" + power + '\'' +
", screenInch=" + screenInch +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
二 定义 Dao 层
Dao 层是真正访问数据库的层,在 JPA 中,这一层只需要继承 JpaRepository<T, ID> 接口,然后在类上使用 @Repository 注解修饰即可拥有一些常规的 CRUD 方法。
package com.qinshou.springbootdemo.dao;
import com.qinshou.springbootdemo.bean.ComputerBean;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* Description:ComputerBean 的 Dao 层
* Author: QinHao
* Date: 2019/7/26 11:05
*/
@Repository
public interface IComputerDao extends JpaRepository<ComputerBean, Integer> {
}
JpaRepository<T, ID> 接口需要指定两个泛型参数,第一个泛型是对应的实体类,第二个参数是主键类型,一般都是 Integer 或者 Long。
然后我们随便找个地方定义一个 IComputerDao 的引用,就会发现我们已经拥有如下方法了:
我们可以看到红色方框内有一些查询、获取数据总数、删除、查询数据是否存在和保存的方法。
三 测试
既然有这么方便的东西,我们就来测试一下它到底好不好用,先写一个 Service 接口,来定义一下它有哪些操作,这里只定义了增删改和三个查询方法。
package com.qinshou.springbootdemo.service;
import com.qinshou.springbootdemo.bean.ComputerBean;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
/**
* Description:ComputerBean 的 Service 层
* Author: QinHao
* Date: 2019/7/26 11:06
*/
public interface IComputerService {
ComputerBean insert(ComputerBean computerBean);
void delete(Integer id);
ComputerBean update(ComputerBean computerBean);
ComputerBean select(Integer id);
List<ComputerBean> selectAll();
Page<ComputerBean> selectList(Pageable pageable);
}
然后写一个这个接口的实现类:
package com.qinshou.springbootdemo.service.impl;
import com.qinshou.springbootdemo.bean.ComputerBean;
import com.qinshou.springbootdemo.dao.IComputerDao;
import com.qinshou.springbootdemo.service.IComputerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* Description:ComputerBean 的 Service 层的实现类
* Author: QinHao
* Date: 2019/7/26 15:32
*/
@Service
public class ComputerServiceImpl implements IComputerService {
@Autowired
private IComputerDao mComputerDao;
@Override
public ComputerBean insert(ComputerBean computerBean) {
return mComputerDao.save(computerBean);
}
@Override
public void delete(Integer id) {
mComputerDao.deleteById(id);
}
@Override
public ComputerBean update(ComputerBean computerBean) {
return mComputerDao.save(computerBean);
}
@Override
public ComputerBean select(Integer id) {
Optional<ComputerBean> optional = mComputerDao.findById(id);
return optional.orElse(null);
}
@Override
public List<ComputerBean> selectAll() {
return mComputerDao.findAll();
}
@Override
public Page<ComputerBean> selectList(Pageable pageable) {
Pageable newPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), new Sort(Sort.Direction.ASC, "id"));
return mComputerDao.findAll(newPageable);
}
}
实现类中调用 Dao 层去完成 CRUD 操作,注意这个类需要使用 @Service 注解来修饰,可以看到其实增和改调用的都是同一个 save 方法,只是它们传入的实体类一个不指定 Id,一个指定 Id 的区别。
然后使用单元测试对这几个方法分别进行测试。
package com.qinshou.springbootdemo.test;
import com.qinshou.springbootdemo.bean.ComputerBean;
import com.qinshou.springbootdemo.service.IComputerService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
* Description:IComputerService 的测试类
* Author: QinHao
* Date: 2019/7/25 19:15
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ComputerDaoTest {
@Autowired
private IComputerService mComputerService;
@Test
public void testInsert() {
mComputerService.insert(new ComputerBean("联想", "i5", 4, 8, 128, "其他", 14.0f));
mComputerService.insert(new ComputerBean("机械革命", "i7", 6, 8, 512, "内置锂离子", 15.6f));
mComputerService.insert(new ComputerBean("联想", "i7", 6, 8, 1024, "内置锂离子", 15.6f));
mComputerService.insert(new ComputerBean("小米", "i5", 4, 8, 512, "内置锂离子", 14.0f));
mComputerService.insert(new ComputerBean("Apple", "i5", 2, 8, 128, "内置锂离子", 13.3f));
mComputerService.insert(new ComputerBean("联想", "i5", 4, 8, 128, "内置锂离子", 15.6f));
mComputerService.insert(new ComputerBean("华为", "i5", 4, 8, 512, "内置锂离子", 14.0f));
}
@Test
public void testDelete() {
mComputerService.delete(1);
}
@Test
public void testUpdate() {
ComputerBean computerBean = new ComputerBean("联想", "i5", 4, 8, 256, "其他", 14.0f);
computerBean.setId(2);
mComputerService.update(computerBean);
}
@Test
public void testSelect() {
ComputerBean computerBean = mComputerService.select(2);
System.out.println(computerBean);
}
@Test
public void testSelectAll() {
List<ComputerBean> computerBeanList = mComputerService.selectAll();
System.out.println(computerBeanList);
}
@Test
public void testSelectList() {
Page<ComputerBean> page = mComputerService.selectList(PageRequest.of(0, 3));
List<ComputerBean> computerBeanList = page.getContent();
System.out.println(computerBeanList);
}
}
执行 testInsert 方法,如果在配置文件中设置了显示 SQL 语句的话,会看到执行的 SQL 语句:
在 MySQL 中也可以看到插入的数据:
后面的方法就不一一贴图了。
四 扩展
在实际运用中,JPA 自带的方法肯定不能满足我们的需求,我们在做操作,特别是查询操作时肯定会有各种各样的限制条件,在扩展方面,JPA 有一套方法命名规则,使我们不用编写 SQL 语句就可以完成一些 WHERE 条件的增加,其实 findById 就是它自己对命名规则的一个体现,在 SQL 中不就是“WHERE ID=?”的意思吗?
JPA 的方法命名规则如下:
其实只要了解这些规则就可以完成一些条件的筛选,这里有几个示例:
@Repository
public interface IComputerDao extends JpaRepository<ComputerBean, Integer>, JpaSpecificationExecutor<ComputerBean> {
/**
* description:根据 brand 查询
* author:QinHao
* date:2019/7/31 18:38
*/
List<ComputerBean> findAllByBrand(String brand);
/**
* description:根据 core 和 memory 删除
* author:QinHao
* date:2019/7/31 18:39
*/
void deleteByCoreAndMemory(Integer core, Integer memory);
/**
* description:查询 screenInch 大于多少的
* author:QinHao
* date:2019/7/31 18:39
*/
List<ComputerBean> findAllByScreenInchGreaterThan(Float screenInch);
/**
* description:查询 cpu 特定字符串开头的
* author:QinHao
* date:2019/7/31 18:39
*/
List<ComputerBean> findAllByCpuStartingWith(String cpuStart);
}
当有特定条件时可以根据上面的表格来拼接 WHERE 子句,这已经可以满足很大一部分的需求了,如果还有更复杂的需求,可以再让 Dao 层继承 JpaSpecificationExecutor<T> 接口,不过这已经属于高级查询了,这个我准备到后面再进行学习。