SpringBoot 整合 MyBatis
—— 既然你都开始整合 Mybatis 了,我相信基本的 SpringBoot 项目创建你自己肯定是可以搞定的,所以我在这里就不多赘述了,话不多 B,让我们直奔主题
MyBatis— 半自动 ORM 框架
现如今,常见的持久层框架有:Hibernate,MyBatis , JPA….
相对于 Hibernate 来说,MyBatis 更容易上手一些
今天我就把 SpringBoot 如何整合 Mybatis 给大家演示一下,共同学习。
如果有没有接触过这两个技术栈的小伙伴,那你们可以先去康康,稍后再来~~。
这是一段来自百度百科的权威解释
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object, 普通的 Java 对象) 映射成数据库中的记录。MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Ordinary Java Objects,普通的 Java 对象)映射成数据库中的记录。
项目框架
Maven 的 pom.xml 文件
需要导入的依赖 — 可以在创建 SpringBoot 项目时可以勾选导入的技术栈
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cody</groupId>
<artifactId>sbootmybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sbootmybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<!-- 此处的配置是识别到 mapper.xml 文件,也可以在 application.properties 中配置 -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
mapper 配置文件
UserMapper.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">
<!-- namespace: 改 mapper.xml 映射文件的唯一标识并且必须和数据处理层的接口的路径相同 -->
<mapper namespace="com.cody.sbootmybatis.sbootmybatis.mapper.UserMapper">
<!-- 必须添加 property 属性,区别于是否加主键 -->
<resultMap id="user" type="User">
<id column="userid" property="userid" javaType="int" ></id>
<result column="username" property="username" javaType="String" ></result>
<result column="userage" property="userage" javaType="int" ></result>
<result column="userpwd" property="userpwd" javaType="String" ></result>
</resultMap>
<!--id 的值必须和数据处理层的接口名一致 -->
<!-- 此处的 User-->
<select id="queryAlluser" resultType="User">
select * from user
</select>
<select id="queryuserbyid" parameterType="int" resultMap="user" resultType="User">
select * from user
<trim suffixOverrides="and" prefixOverrides="and">
<where>
<if test="userid!=null">
and userid = #{userid}
</if>
</where>
</trim>
</select>
<select id="queryuserbyname" resultType="User" parameterType="string" resultMap="user">
select * from user
<trim suffixOverrides="and" prefixOverrides="and">
<where>
<if test="username!=null">
and username = #{username}
</if>
</where>
</trim>
</select>
<update id="UpdUser" parameterType="User">
</update>
<delete id="DelUser"></delete>
<insert id="AddUser" parameterType="User" >
insert into user value (${userid},#{username},${userage},#{userpwd})
</insert>
</mapper>
application.properties 的配置
# 端口的配置
server.port=8081
# 数据库访问配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#将 database_name 改成自己的数据库的名称
spring.datasource.url=jdbc:mysql://localhost:3306/database_name?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
application.yml 的配置
# 配置 Mybatis 的包的别名,在 mapper.xml 文件中 xxxType="xxx" 此处直接可以写实体类的类名即可,不需要将全类名都写上
#<select id="queryAlluser" resultType="User">
# select * from user
# </select>
#<select id="queryAlluser" resultType="com.xxx.xxx.entity.User">
# select * from user
# </select>
mybatis:
type-aliases-package: com.xxx.xxx.entity
#禁用掉模板缓冲,在开发环境中可以实时的观察到 html 页面的更改
spring:
thymeleaf:
cache: false
controller
logincontroller.java
/**
loginController
简单的登录测试
与 index.html 一起使用
*/
import com.cody.sbootmybatis.sbootmybatis.entity.User;
import com.cody.sbootmybatis.sbootmybatis.mapper.UserMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.Map;
/**
* @author xxx
* @date 2020/5/6 - 10:09
*/
@Controller
public class logincontrollor {
@Resource
private UserMapper userMapper;
@RequestMapping(value = "/user/login" ,method = RequestMethod.POST)
@ResponseBody
public String login(@RequestParam("username") String username ,
@RequestParam("password")String password , Map<String,Object> map){User user=userMapper.queryuserbyname(username);
if (username.equals(user.getUsername())&&password.equals(user.getuserpwd())){return "登录成功";}
else{return "登录失败";}
}
}
mycontroller.java
/**
增删改查
*/
package com.cody.sbootmybatis.sbootmybatis.controllor;
import com.cody.sbootmybatis.sbootmybatis.mapper.UserMapper;
import com.cody.sbootmybatis.sbootmybatis.entity.User;
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.RestController;
import java.util.List;
@RestController
@RequestMapping("/user")
public class mycontroller {@Autowired(required = false)
private UserMapper userMapper;
@RequestMapping(value ={"/queryAlluser"},method = RequestMethod.GET)
/**
*
*
* @return List<User>
*
*
* */
public List<User> queryalluser(){List<User> list = userMapper.queryAlluser();
return list;
}
@RequestMapping(value = {"/queryuserbyid"} , method = RequestMethod.GET)
/**
*
*
* @param userid
* @return user
*
*
*/
public User queryuserbyid(String userid){User user = userMapper.queryuserbyid(Integer.parseInt(userid));
return user;
}
@RequestMapping(value = {"/adduser"} , method = RequestMethod.GET)
/**
*
*
* @param useri
* @return user
*
*
*/
public String adduser(User user){boolean flag = userMapper.AddUser((User) user);
return flag ?"success":"fail";
}
@RequestMapping(value = {"/Upuserbyid"} , method = RequestMethod.GET)
/**
*
*
* @param userid
* @return user
*
*
*/
public String Upuser(User user){boolean flag = userMapper.UpdUser((User) user);
return flag?"success":"fail";
}
@RequestMapping(value = {"/Deluserbyid"} , method = RequestMethod.GET)
/**
*
*
* @param userid
* @return user
*
*
*/
public String Deluserbyid(String userid){boolean flag = userMapper.DelUser(Integer.parseInt(userid));
return flag?"success":"fail";
}
}
mapper
package com.cody.sbootmybatis.sbootmybatis.mapper;
import com.cody.sbootmybatis.sbootmybatis.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author Cody
* @date 2020/5/2 - 13:25
* @Decription :
*/
@Mapper
public interface UserMapper {
/**
* 查 读取 (Retrieve)
* fetch data
* @return List<User>
*/
List<User> queryAlluser();
/**
* 查 读取 (Retrieve)
* fetch data by userid
* @param userid
* @return User
*/
/**
@Select("select * from user where userid = #{userid}")
本人喜欢讲 SQL 语句写在 mapper.xml 文件中,这样显得代码比较整齐
在后期开发中,一旦 SQL 语句变得繁琐起来,不利于维护
但是这样用注解的形式也完全可行,毕竟有些人可能写配置文件可能已经快写吐了
*/
User queryuserbyid(int userid);
/**
* 增加 (Create)
* add data by user
* @param user
* @return int
*/
boolean AddUser(User user);
/**
* 删除 (Delete)
* @param id
* @return int
*/
boolean DelUser(int id);
/**
* 更新 (Update)
* @param user
* @return boolean
*/
boolean UpdUser(User user);
User queryuserbyname(String name);
}
前端
将 index.html 文件放在 template 文件夹下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body >
<form method="post" action="/user/login" >
<input name="username" type="text" placeholder="请输入用户名"/>
<input type="password" name="password" />
<input type="submit" th:value="登录"/>
</form>
</body>
</html>
测试
测试登录操作
数据库的信息:
测试 CRUD(即增删改查)
查询全部
根据 ID 查询
增加 User
通过? 和 & 传值
注意事项
1.mappe.xml 文件的路径包括文件名必须同数据层的接口完全一致
2.application.properties 文件和 application.yml 文件不需要都写,二选一即可,只不过这里我都写了而已
使开发更加便利的 IDEA 插件
给大家安利一款 IDEA 插件:MyBatisX
作用: 在接口中写方法可以直接在 mapper.xml 文件中写 CRUD 方法生成相应的标签
图中使用的快捷键是 :==alt+enter==
MyBatis 框架的优点
- 与 JDBC 相比,减少了 50% 以上的代码量。
- MyBatis 是最简单的持久化框架,小巧并且简单易学。
- MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
- 提供 XML 标签,支持编写动态 SQL 语句。
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
MyBatis 框架的缺点
- SQL 语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写 SQL 语句的功底有一定要求。
- SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
SpringBoot 整合 Mybatis 中遇到的 Errors
这是我自己在整合的时候遇到的一些问题,希望自己走过的弯路可以多多帮助到大家。
There was an unexpected error (type=Internal Server Error, status=500).
Error attempting to get column 'u_account' from result set. Cause: java.sql.SQLDataException: Cannot determine value type from string '201577D0510' ; Cannot determine value type from string '201577D0510'; nested exception is java.sql.SQLDataException: Cannot determine value type from string '201577D0510'
org.springframework.dao.DataIntegrityViolationException: Error attempting to get column 'u_account' from result set. Cause: java.sql.SQLDataException: Cannot determine value type from string '201577D0510'
; Cannot determine value type from string '201577D0510'; nested exception is java.sql.SQLDataException: Cannot determine value type from string '201577D0510'
at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy65.selectList(Unknown Source)
解决方法:
添加了 entity 子类中的无参构造方法,异常消失
java.sql.SQLException: Access denied for user ''@'localhost' (using password: NO)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:836)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:246)
解决方法:== 时区报错 ==
更改配置信息:
spring:
datasource:
username: root
password: 123456
# 如果时区报错就需要添加 serverTimezone=UTC
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
使用 @Autowired 注解有错误提示
使用 Spring boot +mybatis 框架时,在 service 实现类中使用 Mapper 类, 给 Mapper 类添加 @Autowired 注解时发现有错误提示:could not autowire,no beans of "XXX" type found,但程序的编译和运行都正常。处理方式:方案一:@Autowired(required = false) 设置 required 属性值为 false,错误消失
方案二:用 @Resource 注解替换 @Autowired 注解,错误消失
@Resource 注解与 @Autowired 注解的异同点
这两个注解都是用作 bean 的注入时使用,都是为一个对象变量省去写 get,set 方法,自动为这个对象注入实例化对象(即注入依赖)注入的方式还是有所区别的:@Autowired 是基于 spring 的注解 org.springframework.beans.factory.annotation.Autowired, 它默认是按类型进行的装配的,如果想要它按名字进行装配则需在 @autowired 下面添加 @qualifier("name")` 注解,都无法找到唯一的一个实现类的时候报错。@Autowired 注解默认情况下必须要求依赖对象必须存在,如果要允许 null 值,则应该设置它的 required 属性为 false,@Resource 是基于 j2ee 的注解(可以减少了与 spring 的耦合),(JDK1.6 以上支持)默认是按名字进行注解,若不指定装配 bean 的名字,当注解写在字段上时,默认取字段名,按照名称查找通过 set 方法进行装配,倘若有多个子类,则会报错。需要注意的是 name 属性一旦指定,就只会按照名称进行装配
解与 @Autowired 注解的异同点
这两个注解都是用作 bean 的注入时使用,都是为一个对象变量省去写 get,set 方法,自动为这个对象注入实例化对象(即注入依赖)注入的方式还是有所区别的:
@Autowired 是基于 spring 的注解 org.springframework.beans.factory.annotation.Autowired, 它默认是按类型进行的装配的,如果想要它按名字进行装配则需在 @autowired 下面添加 @qualifier(“name”)` 注解,都无法找到唯一的一个实现类的时候报错。@Autowired 注解默认情况下必须要求依赖对象必须存在,如果要允许 null 值,则应该设置它的 required 属性为 false,
@Resource 是基于 j2ee 的注解(可以减少了与 spring 的耦合),(JDK1.6 以上支持)默认是按名字进行注解,若不指定装配 bean 的名字,当注解写在字段上时,默认取字段名,按照名称查找通过 set 方法进行装配,倘若有多个子类,则会报错。需要注意的是 name 属性一旦指定,就只会按照名称进行装配
“
The Ending
———- 每天进步“亿”点点 ———-
—–Relyonyouself–