很多同学对“框架”的概念不是很了解,可以了解一下:https://baike.so.com/doc/1863840-1971314.html
问:什么是框架?
答:框架(Framework)是构成软件可复用设计的一组相互协作的类。它规定了你的应用体系结构,定义了整体结构、类、对象的分割,各个部分主要负责分内的工作。举个简单例子:起楼房。要起一栋楼房,工程师们会先在图纸上画好房屋的整体结构,比如大厅、卧室、楼梯间、门窗等,然后采购的人才考虑用什么砖、水泥、沙子等。并不是一上来就砌砖。好的架构,可以把问题划分开来各个解决,让系统开发易于控制、易于管理、易于扩展。这就是系统开发要求的“高内聚、低耦合”的宗旨了。
实现基础的 CRUD
CRUD(增删改查,create、read、update、delete的缩写)操作,接下来,我们对表 user_account 做简单的增删改查功能,以打通数据从“前端——》后台——》数据库——》后台——》前端” 这条闭环之路。
1、我们先在 pom.xml 配置文件里添加 mybatis 依赖和 SpringBoot 的相关依赖,pom.xml 配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.movie</groupId>
<artifactId>haolaiwu-movie</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库MySQL 依赖包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- mybatis 依赖包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- 该 starter 会扫描配置文件中的 DataSource,然后自动创建使用该 DataSource 的 SqlSessionFactoryBean,并注册到 Spring 上下文中 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 阿里巴巴的druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- mybatis generator 自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/mybatis-generator.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
</plugin>
</plugins>
</build>
</project>
说明:
我们在 pom.xml 配置里引入“org.mybatis.spring.boot”依赖,SpringBoot 在启动的时候,会自动检测现有的 DataSource 配置,然后创建并注册 SqlSessionFactory 实例,该实例使用 SqlSessionFactoryBean 将该 DataSource 作为输入进行传递,然后创建 SqlSessionTemplate 实例,自动扫描 mapper 文件,然后注入到 Spring 上下文中。
我们再使用 mybatis-spring-boot-starter ,只需要定义一个 DataSource 即可(可以在配置文件里完成),它会自动创建使用该 DataSource 的。可以关注接下来的 application.yml 配置文件。
2、接着,我们在 database 这个包下,先创建两个包:dao 和 service。然后在 dao 包下创建接口:UserAccountDao,在 service 包下创建类:UserAccountService。如图:
说明:
①dao 包主要存放操作数据库的接口类,里面的方法名对应 mapper.xml 里的 id。
②service 包其实可以不使用,加多一层,主要是为了实现数据库事务的控制(待会再说)。
UserAccountDao 需要增加 @Mapper 注解,以便与 mapper.xml 文件映射。完整代码如下:
package com.movie.database.dao;
import com.movie.entity.UserAccountEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author biandan
* @signature 让天下没有难写的代码
* @create 2019-11-05 下午 10:55
*/
@Mapper
public interface UserAccountDao {
//新增用户信息
int insert(UserAccountEntity userAccountEntity);
//根据主键 id 删除
int delete(@Param("id")Integer id);
//更新用户信息
int update(UserAccountEntity userAccountEntity);
//根据主键 id 查询用户信息
UserAccountEntity selectById(@Param("id")Integer id);
}
说明:UserAccountDao 这个接口我们定义 4 个函数,分别对用户信息实现增删改查。需要注意的是增加 @Mapper 注解。
如果不在 Dao 层添加 @Mapper 注解,可以在启动的类加上:@MapperScan("com.movie.database.dao") 注解,扫描的是Dao层的包。这样就不需要在每个 Dao 类里加入注解 @Mapper 了。
@Param 这个注解非必须,只有一个参数的时候可以不增加,有多个参数的时候最好增加,以保证程序能正确识别参数。
UserAccountService 的完整代码如下:
package com.movie.database.service;
import com.movie.database.dao.UserAccountDao;
import com.movie.entity.UserAccountEntity;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author biandan
* @signature 让天下没有难写的代码
* @create 2019-11-05 下午 10:45
*/
@Service
public class UserAccountService {
//自动注入 UserAccountDao 对象
@Autowired
private UserAccountDao userAccountDao;
//新增用户信息。
//@Transactional(readOnly = false) //readOnly 的事务默认为 false,有读写的操作,可以不设置
public int insert(UserAccountEntity userAccountEntity){
return userAccountDao.insert(userAccountEntity);
}
//删除用户信息
//@Transactional(readOnly = false) //readOnly 的事务默认为 false,有读写的操作,可以不设置
public int delete(@Param("id") Integer id){
return userAccountDao.delete(id);
}
//更新用户信息
//@Transactional(readOnly = false) //readOnly 的事务默认为 false,有读写的操作,可以不设置
public int update(UserAccountEntity userAccountEntity){
return userAccountDao.update(userAccountEntity);
}
//根据主键 id 查询用户信息
@Transactional(readOnly = true)//只读的权限
public UserAccountEntity selectById(@Param("id") Integer id){
return userAccountDao.selectById(id);
}
}
说明:
@Transactional(readOnly = false) 这个注解是控制数据库事务的注解,可以在有需要的方法签名处加上。
接下来,我们修改 UserAccountEntityMapper.xml 这个 mapper 文件。
首先,namespace 这个属性的值就是我们匹配的 Dao 层的某个接口的全路径,即 UserAccountDao 的全路径,我们需要修改。看演示图,如何找到 UserAccountDao 的全路径:对着 UserAccountDao 这个接口鼠标右键,找到“Copy Reference”,然后粘贴到 namespace 这个属性值里。
结果如下。对应的,我们也把 UserAccountEntity 的路径粘贴到 type 的属性值里。
很多人对 insert 语句有疑惑:
因为我们数据库表设计的时候就让主键 id 自增了,所以可以用更简单的写法来操作。让数据库去管理自增长的 id 就好。
完整的 UserAccountEntityMapper.xml 内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.movie.database.dao.UserAccountDao">
<resultMap id="BaseResultMap" type="com.movie.entity.UserAccountEntity">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="user_name" jdbcType="VARCHAR" property="userName" />
<result column="password" jdbcType="VARCHAR" property="password" />
</resultMap>
<!-- 根据主键 id 删除 -->
<delete id="delete" parameterType="java.lang.Integer">
delete from user_account
where id = #{id,jdbcType=INTEGER}
</delete>
<!-- 新增用户信息 -->
<insert id="insert" parameterType="com.movie.entity.UserAccountEntity">
insert into user_account (user_name, password)
values (#{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR})
</insert>
<!-- 更新用户信息 -->
<update id="update" parameterType="com.movie.entity.UserAccountEntity">
update user_account
<set>
<if test="null != userName ">
user_name = #{userName,jdbcType=VARCHAR},
</if>
<if test="null != password ">
password = #{password,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<!-- 根据主键 id 查询用户信息 -->
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select id, user_name, password
from user_account
where id = #{id,jdbcType=INTEGER}
</select>
</mapper>
说明:对于 mybatis 不熟悉的朋友们,可以去以下网址自学:https://mybatis.org/mybatis-3/zh/getting-started.html
还有,对于 Mybatis 逆向工程生成的实体类:UserAccountEntity,基本是定义了数据库的字段属性,并提供 get/set 方法。如果有代码简洁强迫症的同学,可以把注解去掉,顺便加一个序列化ID,在互联网行业做开发这个比较重要。IDEA 工具如何生成序列化ID 可以查看我的博客:https://blog.csdn.net/BiandanLoveyou/article/details/102944164
我们先删除 mybatis 自动生成的 get/set 方法,只留下实体类的属性,然后使用快捷键:Alt + Insert,就可以调出小菜单,选中 Getter and Setter 即可。然后 Ctrl + A 全选属性,点击 OK 即可生成 get/set 方法。我的博客里有讲解 IDEA 的快捷键:https://blog.csdn.net/BiandanLoveyou/article/details/101481418
完整 UserAccountEntity 代码如下(序列化 ID 每个人的都不同,仅供参考):
package com.movie.entity;
import java.io.Serializable;
public class UserAccountEntity implements Serializable{
private static final long serialVersionUID = -6448649226914446277L;
private Integer id;
private String userName;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
接下来,我们编写 manage 层,主要做业务逻辑处理,对外暴露的是接口。在 manage 包下创建接口:UserAccountManage
完整的 UserAccountManage 接口代码如下,注意与 UserAccountService 的差别,二者都是接口,但性质不同,我们定义成布尔类型,便于做逻辑判断:
package com.movie.manage;
import com.movie.entity.UserAccountEntity;
/**
* @author biandan
* @signature 让天下没有难写的代码
* @create 2019-11-06 下午 10:32
*/
public interface UserAccountManage {
//新增用户信息。
boolean insert(UserAccountEntity userAccountEntity);
//删除用户信息
boolean delete(Integer id);
//更新用户信息
boolean update(UserAccountEntity userAccountEntity);
//根据主键 id 查询用户信息
UserAccountEntity selectById(Integer id);
}
接下来,我们编写 manage 的实现类:UserAccountManageImpl
先新建一个包:impl,用来存放所有的实现类。
在 impl 包里创建实现类:UserAccountManageImpl
编写 UserAccountManageImpl 实现类的时候,需要实现 UserAccountMange 接口,然后重写接口的所有方法。这里说个小技巧:如下图,在敲完代码 implements UserAccountManage 后,我们把鼠标的光标点击到 UserAccountManage,然后使用快捷键:Alt + Enter(回车),选中“Implement methods”,就可以重写接口的所有方法了。靠谱!
UserAccountManageImpl 完整代码如下:
package com.movie.manage.impl;
import com.movie.database.service.UserAccountService;
import com.movie.entity.UserAccountEntity;
import com.movie.manage.UserAccountManage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author biandan
* @signature 让天下没有难写的代码
* @create 2019-11-06 下午 10:43
*/
@Service
public class UserAccountManageImpl implements UserAccountManage {
@Autowired
private UserAccountService userAccountService;
//新增用户信息
public boolean insert(UserAccountEntity userAccountEntity) {
boolean flag = false;
try{
int count = userAccountService.insert(userAccountEntity);
if(count > 0){
flag = true;
}
}catch (Exception e){
e.printStackTrace();
}
return flag;
}
//根据主键 id 删除用户信息
public boolean delete(Integer id) {
boolean flag = false;
try{
int count = userAccountService.delete(id);
if(count > 0){
flag = true;
}
}catch (Exception e){
e.printStackTrace();
}
return flag;
}
//更新用户信息
public boolean update(UserAccountEntity userAccountEntity) {
boolean flag = false;
try{
int count = userAccountService.update(userAccountEntity);
if(count > 0){
flag = true;
}
}catch (Exception e){
e.printStackTrace();
}
return flag;
}
//根据主键 id 查询用户信息
public UserAccountEntity selectById(Integer id) {
UserAccountEntity userAccountEntity = null;
try{
userAccountEntity = userAccountService.selectById(id);
}catch (Exception e){
e.printStackTrace();
}
return userAccountEntity;
}
}
说明:
我们需要在接口的实现类(即 UserAccountManageImpl 类)增加注解,@Service 注解表明这是一个业务层的实现类,而不需要在接口(即 UserAccountManage 类)增加注解,这样符合热拔插式。如果后期有实现类的更改,只需要在实现类增加注解即可。
接下来,我们编写 controller 层:UserAccountController,主要用来处理客户端的请求,并获取业务层 manage 返回的信息。
UserAccountController 完整代码如下:
package com.movie.controller;
import com.movie.entity.UserAccountEntity;
import com.movie.manage.UserAccountManage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @author biandan
* @signature 让天下没有难写的代码
* @create 2019-11-06 下午 11:00
*/
@RestController
@RequestMapping(value = "/user")
public class UserAccountController {
@Autowired
private UserAccountManage userAccountManage;
//新增用户信息
@ResponseBody
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Map<String,Object> addUserInfo(HttpServletRequest request) {
Map<String,Object> map = new HashMap<>();
map.put("code",-1);
map.put("message","新增用户信息失败!");
try {
//从 request 对象中获取用户名、密码
String userName = request.getParameter("userName");
String password = request.getParameter("password");
//创建用户对象,并注入属性
UserAccountEntity entity = new UserAccountEntity();
entity.setUserName(userName);
entity.setPassword(password);
//调用 UserAccountManage 接口的新增用户方法
boolean flag = userAccountManage.insert(entity);
if(flag){//成功
map.put("code",0);
map.put("message","新增用户信息成功!");
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
//根据 id 删除用户信息
@ResponseBody
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public Map<String,Object> deleteUserInfo(HttpServletRequest request) {
Map<String,Object> map = new HashMap<>();
map.put("code",-1);
map.put("message","删除用户信息失败!");
try {
//从 request 对象中获取用户名主键 id
int id = Integer.parseInt(request.getParameter("id"));
//调用 UserAccountManage 接口删除方法
boolean flag = userAccountManage.delete(id);
if(flag){//成功
map.put("code",0);
map.put("message","删除用户信息成功!");
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
//修改用户信息
@ResponseBody
@RequestMapping(value = "/update", method = RequestMethod.POST)
public Map<String,Object> updateUserInfo(HttpServletRequest request) {
Map<String,Object> map = new HashMap<>();
map.put("code",-1);
map.put("message","修改用户信息失败!");
try {
//从 request 对象中获取用户名、密码
Integer id = Integer.parseInt(request.getParameter("id"));
String userName = request.getParameter("userName");
String password = request.getParameter("password");
//创建用户对象,并注入属性
UserAccountEntity entity = new UserAccountEntity();
entity.setId(id);
entity.setUserName(userName);
entity.setPassword(password);
//调用 UserAccountManage 接口的修改用户方法
boolean flag = userAccountManage.update(entity);
if(flag){//成功
map.put("code",0);
map.put("message","修改用户信息成功!");
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
//根据主键 id 查询用户信息
@ResponseBody
@RequestMapping(value = "/select", method = RequestMethod.POST)
public UserAccountEntity selectUserInfo(HttpServletRequest request) {
UserAccountEntity entity = null;
try {
//从 request 对象中获取用户名主键 id
int id = Integer.parseInt(request.getParameter("id"));
//调用 UserAccountManage 接口的查询用户方法
entity = userAccountManage.selectById(id);
} catch (Exception e) {
e.printStackTrace();
}
return entity;
}
}
说明:如果使用了 @RestController 来修饰一个类,则方法不需要再使用 @ResponseBody 修饰。
@Controller:修饰class,用来创建处理http请求的对象
@RestController:Spring4之后加入的注解,原来在 @Controller 中返回 json 需要 @ResponseBody 来配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,默认返回 json 格式。
@RequestMapping:配置 url 映射
增删改 这3个方法都返回一个 map 对象,map 对象里存了两个 key,如果成功,会把失败对应的 key 的值覆盖。主要给大家体验一下默认返回 json 格式的数据,以及企业中标准接口协议里如何封装返回信息给调用者。
接下来,我们创建 SpringBoot 启动类:HaoLaiWuMovieApplication,注意存放的位置,在 movie 包下。
HaoLaiWuMovieApplication 完整代码:
package com.movie;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author biandan
* @signature 让天下没有难写的代码
* @create 2019-11-06 下午 11:28
*/
@SpringBootApplication
public class HaoLaiWuMovieApplication {
public static void main(String[] args) {
SpringApplication.run(HaoLaiWuMovieApplication.class,args);
}
}
接下来,我们创建 application.yml 配置文件,配置程序启动时加载到 spring 容器的对象。
# 配置服务相关信息
server:
# 端口号
port: 8080
# 服务名称和数据源配置
spring:
application:
name: haolaiwu
datasource:
# 使用阿里巴巴的 druid 数据源
druid:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/movie?characterEncoding=utf-8
username: root
password: 123456
# mybatis 配置
mybatis:
# Mybatis扫描的mapper文件
mapper-locations: classpath:mapper/*.xml
# 扫描哪个包下的对象
type-aliases-package: com.movie.entity
# Mybatis配置文件
config-location: classpath:mybatis-config.xml
最后,我们在 resources 文件夹下创建 mybatis-config.xml 配置文件,与 application.yml 同一级
mybatis-config.xml 配置文件完整代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局配置参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存。 -->
<setting name="cacheEnabled" value="true"/>
<!-- debug 模式打印 sql 语句 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
OK,万事俱备只欠东风。我们来运行启动 HaoLaiWuMovieApplication:
项目启动成功:
我们使用 postman 工具,下载 postman 地址:https://www.getpostman.com/downloads/
测试接口:
1、新增用户信息:
我们看到控制台输出 SQL 语句,这主要是我们在 mybatis-config.xml 配置文件加了一行配置代码:
<setting name="logImpl" value="STDOUT_LOGGING"/>
控制台输出信息:
==> Preparing: insert into user_account (user_name, password) values (?, ?)
==> Parameters: biandan(String), nice123(String)
<== Updates: 1
去数据库查看:
postman 返回结果:
2、修改用户信息(必须传递主键 id):
postman 输出:
查看数据库:
3、根据主键 id 查询用户信息:
postman 结果:
4、根据主键 id 删除用户信息:
postman 显示结果:
OK,至此,我们完成了一个项目框架的搭建,并且实现了对数据库表的增删改查。这是最基础的一步,也是最重要的一步。千里之行始于足下,未来可期啊!