1、项目技术
开发工具:Idea
开发框架:SpringBoot(Spring + Spring MVC + MyBatis)
数据库: Mysql
2、实现功能
1、实现注册
2、下面只写到了实现网页查看数据是否与数据库连接,页面还没实现,下篇讲解从mapper<service<controller三层查找数据后,如何把数据响应回页面。
3、项目架构
4、下载地址
5、开始项目
5.1、注册mapper持久层 > service业务层 > controller控制器
第一篇已经创建数据库,成功:
5.2、注册mapper层 -- 实体类
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 实体类的基类
*/
@Data
abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = -3122958702938259476L;
private String createdUser;
private Date createdTime;
private String modifiedUser;
private Date modifiedTime;
}
import lombok.Data;
/**
* 用户实体类
*/
@Data
public class User extends BaseEntity{
private static final long serialVersionUID = -6992489078211958584L;
private Integer uid;
private String username;
private String password;
private String salt;
private Integer gender;
private String phone;
private String email;
private String avatar;
private Integer isDelete;
}
持久层开发
(a)规划需要执行的sql语句
此次需要实现的功能是“注册”,其本质是向数据表中插入新的数据,需要执行的SQL语句大致是:
insert into t_user (除了uid以外的所有字段) values (匹配的值列表);
在执行插入数据之前,还应该检查该用户名是否已经注册过,所以,还需要执行查询操作,其SQL语句大致是:
select * from t_user where username=?
(b) 接口与抽象方法
Integer insert(User user);
User findByUsername(String username);
第一次配置持久层接口,需要给SprigBoot指定持久层接口的位置,在启动类里面添加:
在**application.properties**文件中添加关于XML映射文件的位置的配置信息:
mybatis.mapper-locations=classpath:mappers/*.xml
(c)、编写UserMapper
import com.example.springbootdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
/**
* 用户注册,添加用户
* @param user
* @return
*/
Integer insert(User user);
/**
* 用户注册,判断用户名是否被占用
* @param username
* @return
*/
User findByUsername(String username);
}
(d)、配置映射
在配置映射时,注意:
mapper – namespace对应的是定义的mapper接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<!-- 指定mapper接口 -->
<mapper namespace="com.example.springbootdemo.mapper.UserMapper">
<!-- 指定User用户实体类 -->
<resultMap id="UserEntityMap"
type="com.example.springbootdemo.entity.User">
<id column="uid" property="uid"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="salt" property="salt"/>
<result column="gender" property="gender"/>
<result column="phone" property="phone"/>
<result column="email" property="email"/>
<result column="avatar" property="avatar"/>
<result column="is_delete" property="isDelete"/>
<result column="created_user" property="createdUser"/>
<result column="created_time" property="createdTime"/>
<result column="modified_user" property="modifiedUser"/>
<result column="modified_time" property="modifiedTime"/>
</resultMap>
<!-- 插入用户数据 -->
<!-- Integer insert(User user) -->
<insert id="insert"
useGeneratedKeys="true"
keyProperty="uid">
INSERT INTO t_user (
username, password,
salt, gender,
phone, email,
avatar, is_delete,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{username}, #{password},
#{salt}, #{gender},
#{phone}, #{email},
#{avatar}, #{isDelete},
#{createdUser}, #{createdTime},
#{modifiedUser}, #{modifiedTime}
)
</insert>
<!-- 根据用户名查询用户数据详情 -->
<!-- User findByUsername(String username) -->
<select id="findByUsername"
resultMap="UserEntityMap">
SELECT
*
FROM
t_user
WHERE
username=#{username}
</select>
</mapper>
业务层service层
(a) 规划异常
用户名是否被占用,如果被占用,则抛出异常:
UsernameDuplicateException异常类;
插入数据时,返回的受影响的行数可能不是1,则插入数据失败,也需要抛出异常:
InsertException异常类
异常的基类:
ServiceException,继承自RuntimeException。
(b) 接口与抽象方法
void register(User user);
© 实现业务
这个时候创建个工具类,因为MD5加密可以放出来,在路径下,新建util包,存放MD5Util
import org.springframework.util.DigestUtils;
/**
* md5加密工具类
*
* @author Administrator
*
*/
public class MD5Util {
public static String getMd5Password(String password, String salt) {
// 加密规则:
// 在密码的左右两侧各拼接一次盐值
// 执行5次加密
String str = salt + password + salt;
for (int i = 0; i < 5; i++) {
str = DigestUtils
.md5DigestAsHex(str.getBytes())
.toUpperCase();
}
return str;
}
}
业务实现
import com.example.springbootdemo.entity.User;
import com.example.springbootdemo.mapper.UserMapper;
import com.example.springbootdemo.service.IUserService;
import com.example.springbootdemo.service.ex.InsertException;
import com.example.springbootdemo.service.ex.UsernameDuplicateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void register(User user) {
// 先查询用户输入username
String username = user.getUsername();
// 根据用户输入username查找数据库有没有数据
User user1 = userMapper.findByUsername(username);
// 当用户输入不为null,说明数据库中存在此用户名!
if (user1 != null) {
// 抛出用户名已经占用异常
throw new UsernameDuplicateException("尝试的用户名" + username + "已经被占用!");
}
// 用户名没有被占用
String salt = UUID.randomUUID().toString().toUpperCase();
// 基于原密码和盐值进行加密处理
String md5Password = MD5Util.getMd5Password(user.getPassword(), salt);
// 补全数据:将盐值、新密码设置到salt、password属性
user.setSalt(salt);
user.setPassword(md5Password);
user.setIsDelete(0);
user.setCreatedTime(new Date());
user.setCreatedUser(username);
user.setModifiedTime(new Date());
user.setModifiedUser(username);
user.setEmail(user.getEmail());
user.setPhone(user.getPhone());
Integer rows = userMapper.insert(user);
if (rows != 1) {
throw new InsertException("添加用户"+username+"未成功!!");
}
}
}
这个时候就完成注册的业务层了!!
控制器Controller
(a) 处理异常
当发生异常时,创建JsonResult返回信息给控制器响应结果类型,并声明属性。
这个时候,新的知识点又来了,util工具类,在新建JsonResult.
<T> 什么意思呢?
泛型,<T> T表示返回值是一个泛型,传递啥,就返回啥类型的数据,而单独的T就是表示限制你传递的参数类型,
通过一个泛型的返回方式,获取每一个集合中的第一个数据, 通过返回值<T> T 和T的两种方法实现
@Data
public class JsonResult<T> {
private Integer state; // 响应状态码
private String message; // 响应信息
private T data; // 响应数据
public JsonResult() {
super();
}
public JsonResult(Integer state) {
super();
this.state = state;
}
}
import com.example.springbootdemo.service.ex.InsertException;
import com.example.springbootdemo.service.ex.ServiceException;
import com.example.springbootdemo.service.ex.UsernameDuplicateException;
import com.example.springbootdemo.util.JsonResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* 控制器的基类,用于判断异常类型返回State状态码
*/
@ControllerAdvice // 全局异常捕捉处理
public class BaseController {
@ExceptionHandler(ServiceException.class)
public JsonResult<Void> handleException(Throwable ex) {
JsonResult<Void> jsonResult = new JsonResult<>();
if (ex instanceof UsernameDuplicateException) {
jsonResult.setState(2);
} else if (ex instanceof InsertException) {
jsonResult.setState(3);
}
return jsonResult;
}
}
注册的Controller:UserController
import com.example.springbootdemo.entity.User;
import com.example.springbootdemo.service.IUserService;
import com.example.springbootdemo.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private IUserService userService;
@RequestMapping("/reg")
public JsonResult<Void> register(User user){
userService.register(user);
return new JsonResult<>(1,"用户"+user.getUsername()+"注册成功!");
}
}
浏览器输入:
http://localhost:8080/user/reg?username=json&password=1234&gender=1&phone=13800138001&email=[email protected]
成功注册!
用户名被占用!!
注册前端界面
注意事项
持久层需要做的事情:
1、在启动类添加mapper接口对应的地址
MapperScan(" ")
2、在application.properties添加添加关于XML映射文件的位置的配置信息:
mybatis.mapper-locations=classpath:mappers/*.xml
业务层注意的事情
@Service注解加在service实现层Impl
扩展------SpringBoot项目
自带测试工具
Spring测试需要test项目前添加@RunWith(SpringRunner.class)
import com.example.springbootdemo.entity.User;
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.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired(required=true)
private UserMapper mapper;
@Test
public void insert() {
User user = new User();
user.setUsername("salt001");
user.setPassword("1234");
user.setEmail("2212");
user.setPhone("23123123132");
System.err.println(user); // uid=null
Integer rows = mapper.insert(user);
System.err.println("rows=" + rows);
System.err.println(user); // uid=2
}
//根据用户名查询用户详情
@Test
public void findByUsername() {
String username = "spring1";
User user = mapper.findByUsername(username);
System.err.println(user);
}
}
报错和解决方法
(a)刚开始做项目爆了一堆错,测试mapper的时候如下:
2020-02-04 17:09:07.284 ERROR 5164 --- [ main] o.s.test.context.TestContextManager :
Caught exception while allowing TestExecutionListener
[org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@1d296da]
to prepare test instance [com.example.springbootdemo.mapper.UserMapperTest@d4df0e]
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'com.example.springbootdemo.mapper.UserMapperTest':
Unsatisfied dependency expressed through field 'mapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.springbootdemo.mapper.UserMapper' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
解决办法:
MpperScan(” “)定义接口位置错了,少写一个字母,哎,大家以后也要注意检查
(b)service层错误
我说我加了@Service注解了,怎么报错,之前加在service接口上,后来把@Service加在ServiceImpl实现层
错误没了,大家注意,嘻嘻嘻!好歹我找错时间不是很长。
报错信息:
Unsatisfied dependency expressed through field 'userService'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'com.example.springbootdemo.service.IUserService' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
解决办法:
在service实现层加上@Service注解
所用注解
@Service
service实现层的注解,是把spring容器中的bean进行实例化,也就是等同于new操作,只有实现类是可以进行new实例化的,而接口则不能,所以是加在实现类上的。不信你标注到接口上,会报BeanCreationException的。
@MapperScan 作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加
@Mapper
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类 添加位置:接口类上面
@Autowired
自动装配,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@RestController
相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
@Controller
在对应的方法上,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面
若返回json等内容到页面,则需要加@ResponseBody注解
@ResponseBody
将java对象转为json格式的数据
@ControllerAdvice == @RestControllerAdvice
是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被
@RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理
@RequestMapping
一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@Data
注解的主要作用是提高代码的简洁,使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法;
@Mapper
标注mapper持久层接口