这里我们要用三种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上。