这里我们要用三种DAO实现方法来编写增删改查项目,大部分的内容之前都编写过,这里就是看如何使用Spring Boot来支撑这个项目和配置一些DAO方面的内容。
项目设计
了解了基础知识之后,就是用Spring Boot来写一个增删改查程序了,而且要用到REST API,设计如下:
方法 |
路径 |
操作 |
POST |
/api/employees |
创建新对象 |
GET |
/api/employees |
获取全部对象 |
GET |
/api/employees/{employeeId} |
获取某个对象 |
PUT |
/api/employees |
修改某个对象 |
DELETE |
/api/employees/{employeeId} |
删除某个对象 |
我们的开发步骤如下:
- 创建数据库和表
- 使用Spring Boot Initializer来创建项目
- 编写查询功能
- 编写创建功能
- 编写修改功能
- 编写删除功能
在业务逻辑上,依然分为Controller,Service和DAO三层。所有的配置均采用Java和注解,不采用XML配置方式。
创建数据库
创建数据库的脚本如下:
CREATE DATABASE IF NOT EXISTS `employee_directory`;
USE `employee_directory`;
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(45) DEFAULT NULL,
`last_name` varchar(45) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO `employee` VALUES
(1,'Leslie','Andrews','leslie@gmail.com'),
(2,'Emma','Baumgarten','emma@sina.com'),
(3,'Avani','Gupta','avani@163.com'),
(4,'Yuri','Petrov','yuri@aliyun.com'),
(5,'Juan','Vega','juan@v2ex.com');
这个表很简单。
创建Spring Boot项目
到https://start.spring.io/,依次选择Maven项目,Java 8,Spring最新稳定版,输入自己的包名和项目名。然后在依赖里选择如下:
- Web
- JPA
- DevTools
- MySQL
就可以下载项目文件了。之后导入到Intellij里等待配置结束。
在导入项目的时候,如果遇到pom.xml文件中提示failed to read artifact descriptor错误,按照这里的方法,将Intellij的Maven中的Always update snapshots
打开即可
注意此时还不能运行项目,如果运行的话,会被提示必须配置一个数据库驱动。
DAO层的创建
在之前的项目中,我们的DAO层使用了Spring提供的类,注入连接池,最终使用了Hibernate的Session Factory对象。在XML里或者Java配置类里写上数据库连接的内容。
现在无需这么麻烦,Spring Boot会自动从pom.xml中读取JDBC Driver(MySQL)和ORM(JPA依赖),我们只需要在application.properties中配置一下数据库的具体连接信息即可:
spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory?useSSL=false&serverTimezone=UTC
spring.datasource.username=springstudent
spring.datasource.password=springstudent
除了这些基本信息,还有很多属性可以配置,看文档。
在这里还有一个需要说明的是,Spring Boot会自动创建DataSource
,EntityManager
等Bean,可以直接将其注入到自己编写的DAO层中。
这里的EntityManager
是一个Java Persistence API(JPA,Java持久化标准)的类,实际上就是一个包装了Hibernate的SessionFactory的对象。关于JPA也就是Java持久化标准,其实主要就来自于Hibernate,这一块将来还要去读一下《Java Persistence with Hibernate》这本书了。
在这里一次性把DAO学透,将采用三种方式来编写DAO:
- 使用EntityManager对象,但使用Hibernate的API
- 直接使用EntityManager对象和标准JPA API
- 使用Spring Data 的JPA实现
先来创建Employee类用于映射对应的数据表:
package cc.conyli.sbcrud.entity;
import javax.persistence.*;
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
public Employee(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public Employee() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
'}';
}
}
Entity类已经很熟悉了,要设置空参构造方法和setter/getter方法。
然后是DAO的接口:
package cc.conyli.sbcrud.dao;
import cc.conyli.sbcrud.entity.Employee;
import java.util.List;
public interface EmployeeDAO {
public List<Employee> findAll();
}
接口先只写一个方法。
使用Hibernate的API来编写DAO层
来编写基于Hibernate API的实现类:
package cc.conyli.sbcrud.dao;
import cc.conyli.sbcrud.entity.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.hibernate.*;
import javax.persistence.EntityManager;
import java.util.List;
@Repository
public class EmployeeDAOHibernateImpl implements EmployeeDAO {
//注入EntityManager
private EntityManager entityManager;
//EntityManager会被Spring Boot自动创建
@Autowired
public EmployeeDAOHibernateImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public List<Employee> findAll() {
//获取Hibernate的SessionFactory
Session currentSession = entityManager.unwrap(Session.class);
Query<Employee> query = currentSession.createQuery("from Employee", Employee.class);
List<Employee> employees = query.getResultList();
return employees;
}
}
这里要注意的是,可以通过EntityManager对象来获取hibernate的Session实现类。Hibernate里的Session和SessionFactory都是接口。
之后创建Query查询来获取列表结果,不过这里要注意的是,IDE提示Query已经过期。具体Hibernate的API,还要单独看一看。
之后为了验证是否OK,来编写一个Controller返回JSON。
package cc.conyli.sbcrud.rest;
import cc.conyli.sbcrud.dao.EmployeeDAO;
import cc.conyli.sbcrud.entity.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api")
public class EmployeeRESTController {
private EmployeeDAO employeeDAO;
@Autowired
public EmployeeRESTController(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
@GetMapping("/employees")
public List<Employee> getEmployees() {
return employeeDAO.findAll();
}
}
启动项目,访问http://localhost:8080/api/employees
,可以发现成功的显示除了JSON字符串。
之后我们把DAO接口的方法补全,编写好完整的HibernateDAO类和控制器方法,这样可以在后边顺利切换不同的DAO实现。
完整的DAO层
完整的DAO接口:
package cc.conyli.sbcrud.dao;
import cc.conyli.sbcrud.entity.Employee;
import java.util.List;
public interface EmployeeDAO {
public List<Employee> findAll();
public Employee findById(int id);
public void save(Employee employee);
public void deleteById(int id);
}
完整的HibernateDAO实现类:
package cc.conyli.sbcrud.dao;
import cc.conyli.sbcrud.entity.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.hibernate.*;
import javax.persistence.EntityManager;
import java.util.List;
@Repository
public class EmployeeDAOHibernateImpl implements EmployeeDAO {
//注入EntityManager
private EntityManager entityManager;
//EntityManager会被Spring Boot自动创建
@Autowired
public EmployeeDAOHibernateImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public List<Employee> findAll() {
//获取Hibernate的SessionFactory
Session currentSession = entityManager.unwrap(Session.class);
Query<Employee> query = currentSession.createQuery("from Employee", Employee.class);
return query.getResultList();
}
@Override
public Employee findById(int id) {
Session currentSession = entityManager.unwrap(Session.class);
return currentSession.get(Employee.class, id);
}
@Override
public void save(Employee employee) {
Session currentSession = entityManager.unwrap(Session.class);
currentSession.saveOrUpdate(employee);
}
@Override
public void deleteById(int id) {
Session currentSession = entityManager.unwrap(Session.class);
Employee employee = currentSession.get(Employee.class, id);
currentSession.delete(employee);
}
}
完整的Service层
既然要三层,就创建Service类,也都是套路代码了,接口+实现类:
package cc.conyli.sbcrud.service;
import cc.conyli.sbcrud.entity.Employee;
import java.util.List;
public interface EmployeeService {
public List<Employee> findAll();
public Employee findById(int id);
public void save(Employee employee);
public void deleteById(int id);
}
package cc.conyli.sbcrud.service;
import cc.conyli.sbcrud.dao.EmployeeDAO;
import cc.conyli.sbcrud.entity.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class EmployeeServiceImpl implements EmployeeService {
private EmployeeDAO employeeDAO;
@Autowired
public EmployeeServiceImpl(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
@Override
@Transactional
public List<Employee> findAll() {
return employeeDAO.findAll();
}
@Override
@Transactional
public Employee findById(int id) {
return employeeDAO.findById(id);
}
@Override
@Transactional
public void save(Employee employee) {
employeeDAO.save(employee);
}
@Override
@Transactional
public void deleteById(int id) {
employeeDAO.deleteById(id);
}
}
完整的Controller层
然后需要修改控制器,注入Service层的对象,完整的控制器如下,也都是套路代码:
package cc.conyli.sbcrud.rest;
import cc.conyli.sbcrud.entity.Employee;
import cc.conyli.sbcrud.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
public class EmployeeRESTController {
private EmployeeService employeeService;
@Autowired
public EmployeeRESTController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
@GetMapping("/employees")
public List<Employee> getEmployees() {
return employeeService.findAll();
}
@GetMapping("/employees/{employeeId}")
public Employee getEmployee(@PathVariable int employeeId) {
Employee employee = employeeService.findById(employeeId);
if (employee == null) {
throw new RuntimeException("Employee id {{" + employeeId + "}} NOT FOUND");
}
return employee;
}
@PostMapping("/employees")
public Employee addEmployee(@RequestBody Employee employee) {
employee.setId(0);
employeeService.save(employee);
return employee;
}
@PutMapping("/employees")
public Employee updateEmployee(@RequestBody Employee employee) {
//无需先判断是否存在,否则会造成重复引用同一个数据,会报错。
employeeService.save(employee);
return employee;
}
@DeleteMapping("/employees/{employeeId}")
public Employee deleteEmployee(@PathVariable int employeeId) {
Employee targetEmployee = employeeService.findById(employeeId);
if (targetEmployee == null) {
throw new RuntimeException("Employee id {{" + employeeId + "}} NOT FOUND");
}
employeeService.deleteById(employeeId);
return targetEmployee;
}
}
之后用Postman测试,发现可以正常工作,我们就写好了基于Hibernate API DAO的增删改查项目。
之后我们要用另外两种方式来实现DAO,并且将项目切换到这两个DAO上。