孤独的人有自己的泥沼。
1、MyBatis简述
![](https://i-blog.csdnimg.cn/blog_migrate/469411962c80b5391746da8d78b0c3ef.png)
- MyBatis 是一款优秀的持久层框架,用于简化JDBC开发。(半自动化的ORM框架)
- 它支持自定义SQL、存储过程以及高级映射。
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和Java的POJO(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录。
- MyBatis 是 Apache的一个开源项目iBatis,在2010年6月 Apache 将项目交与Google进行管理,更名MyBatis。于2013年11月迁移到GitHub上。
- Mybatis官网:https://mybatis.org/mybatis-3/zh/index.html
- 项目地址:https://github.com/mybatis/mybatis-3/releases
- 目前最新版本是:3.5.11,其发布时间是2022年9月18日。
MyBatis的缺点:
- 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
- SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
- 框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
- 二级缓存机制不佳。
什么是框架?
- 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。
- 在架构的基础之上构建软件编写更加高效、规范、通用、可扩展。
软件设计:(open-close开闭原则)尽量不修改源代码,对程序进行扩展。
- 对扩展是开放的。
- 对修改源码是关闭的。
MyBatis的安装:
1、使用 MyBatis, 只需将 mybatis-x.x.x.jar
文件置于类路径(classpath)中即可。
2、如果使用 Maven 来构建项目,则需将下面的依赖引入 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2、快速入门
1、创建一个普通的Maven项目
2、引入依赖坐标
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
3、导入SQL脚本
-- 创建数据库
drop database if exists `mybatis`;
create database `mybatis` character set = utf8;
use mybatis;
-- 创建数据表
create table `user`(
`id` int auto_increment primary key comment '编号',
`name` varchar(20) unique comment '姓名', -- 唯一约束
`pwd` varchar(20) comment '密码',
`gender` char(1) comment '性别',
`addr` varchar(30) comment '家庭住址'
)engine=innodb default charset=utf8;
-- 插入数据
insert into user(name,pwd,gender,addr) values
("张三","123456",'男',"北京"),
("李四","123456",'女',"南京"),
("王五","777777",'男',"杭州"),
("赵六","123456",'女',"温州"),
("陈七","666666",'男',"黑河");
4、编写实体类
public class User {
private Integer id;
private String name;
private String pwd;
private char gender;
private String addr;
public User() {
}
public User(Integer id, String name, String pwd, char gender, String addr) {
this.id = id;
this.name = name;
this.pwd = pwd;
this.gender = gender;
this.addr = addr;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
", gender=" + gender +
", addr='" + addr + '\'' +
'}';
}
}
5、编写mapper接口
public interface UserMapper {
// 查询所有用户
List<User> findAll();
}
6、编写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-->
<mapper namespace="com.baidou.mapper.UserMapper">
<!--select语句 , id:是这里的唯一标识用来绑定接口中对应的方法 ,resultType:返回值类型-->
<select id="findAll" resultType="com.baidou.pojo.User">
select * from mybatis.user
</select>
</mapper>
7、编写mybatis核心配置文件
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>
<!--可以多环境切换-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--映射器-->
<mappers>
<!--之后mapper的配置文件需要在这里注册-->
<mapper resource="com/baidou/mapper/UserMapper.xml"/>
</mappers>
</configuration>
8、编写日志配置文件
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 配置文件修改时重新加载,默认true -->
<configuration scan="true">
<!--控制台输出-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 输出的日志格式 -->
<pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg%n</pattern>
</encoder>
</appender>
<!--level:用来设置日志输出级别,有OFF FATAL ERROR WARN INFO DEBUG TRACE ALL,默认DEBUG-->
<logger name="com.baidou" level="DEBUG" additivity="false">
<appender-ref ref="Console"/>
</logger>
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>
9、测试
public class ApplicationTest {
public static void main(String[] args) throws IOException {
// 1、加载Mybatis核心配置文件,获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取对应的SqlSession对象,用来执行sql语句
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、获取mapper,来调用mapper中的方法
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findAll();
for (User user:users){
System.out.println(user);
}
//4、关闭SqlSession
sqlSession.close();
}
}
控制台输出结果如下:
10、优化(编写MyBatis工具类)
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过SqlSessionFactory来获取SqlSession对象
* SqlSession:提供了在数据库执行SQL命令所需的所有方法。
*
* @return SqlSession
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(); //connection
}
}
测试:
public class MyBatisTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAll();
userList.forEach(System.out::println);
}
}
测试结果如下:
项目的目录结构:
MyBatis执行流程:
总结:
- 导入MyBatis依赖
- 编写核心配置文件,mybatis-config.xml
- 编写实体、接口
- 编写mapper.xml(mapper 对应mapper.xml)
- 在核心配置文件中注册这个mapper.xml
- 编写工具类(用来获取SqlSession实例)
- 通过工具类得到SqlSession,然后通过SqlSession来获取我们的mapper,之后通过这个mapper来调用它的方法
- 关闭SqlSession
3、Mapper代理开发
1、定义与SQL映射文件同名的Mapper接口,并将Mapper接口和SQL映射文件放置在同一目录下;
2、设置SQL映射文件的namespace属性为Mapper接口全限定名(包名.接口名);
3、在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致;
4、编码操作
- 通过SqlSession的getMaper()方法获取Mapper接口的代理对象;
- 调用对应方法完成sql的执行;
【扩展】如果Mapper接口和SQL映射文件名称相同,并且在同包下,可以使用包扫描的方式简化SQL映射文件的加载:
<!-- 加载sql的映射文件 -->
<mappers>
<!--<mapper resource="com/baidou/mapper/UserMapper.xml"/>-->
<package name="com.baidou.mapper"/>
</mappers>
【示例】测试CRUD操作
1、编写接口
public interface UserMapper {
/**
* 获取全部的用户
*/
List<User> getUserList();
/**
* 通过ID获取用户
* @param id
* @return
*/
List<User> getUserById(int id);
/**
* 添加用户
* @param user
* @return
*/
int addUser(User user);
/**
* 修改用户信息
* @param user
* @return
*/
int updateUser(User user);
/**
* 删除用户
* @param id
* @return
*/
int deleteUser(int id);
}
2、编写SQL映射文件
<?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:绑定一个对应的Dao/Mapper接口 -->
<mapper namespace="com.baidou.mapper.UserMapper">
<!-- select查询语句,id:绑定对应方法名,resultType:返回值类型 -->
<select id="getUserList" resultType="com.baidou.pojo.User">
select * from mybatis.user ;
</select>
<select id="getUserById" resultType="com.baidou.pojo.User" parameterType="int">
select * from mybatis.user where id = #{id} ;
</select>
<insert id="addUser" parameterType="com.baidou.pojo.User">
insert into mybatis.user (name, pwd) values(#{name},#{pwd}) ;
</insert>
<update id="updateUser" parameterType="com.baidou.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id} ;
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id =#{id} ;
</delete>
</mapper>
3、测试
public class UserMapperTest {
// 查询所有用户
@Test
public void test() {
// 获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 方式一:getmapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
// 方式二:
// List<User> userList = sqlSession.selectList("com.baidou.dao.UserDao.getUserList");
//for (User user : userList) {
// System.out.println(user);
//}
userList.forEach(System.out::println);
//关闭SqlSession
sqlSession.close();
}
// 查询指定id的用户
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> user = mapper.getUserById(1);
System.out.println(user);
// 关闭SqlSession
sqlSession.close();
}
// 添加用户
@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.addUser(new User("风清扬", "fenqingyang",'男',"金庸")) > 0 ? "添加成功" : "添加失败");
// 增删改需要提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
// 修改用户
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.updateUser(new User(3, "王富贵", "wangfugui",'女',"土耳其")) > 0 ? "修改成功" : "修改失败");
// 提交事务
sqlSession.commit();
sqlSession.close();
}
// 删除用户
@Test
public void deleteUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.deleteUser(3) > 0 ? "删除成功" : "删除失败");
// 提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
}
注意:增删改需要提交事务!!!
4、MyBatis核心配置文件
核心配置文件(mybatis-config.xml)的顶层结构如下:
注:配置各个标签时,需要遵守前后顺序!
4.1、properties(属性)
- properties属性可以引用外部配置文件;
- properties属性也可以在内部增加一些属性配置;
- 如果两个文件有共同的字段,会优先使用外部配置文件的!
1、编写db.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8
username=root
password=123456
2、在mybatis-config.xml中引用db.properties
<?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>
<!--引入外部配置文件-->
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--映射器-->
<mappers>
<!--<mapper resource="com/baidou/mapper/UserMapper.xml"/>-->
<package name="com.baidou.mapper"/>
</mappers>
</configuration>
4.2、environments(环境配置)
- MyBatis 可以配置多种环境,但每个 SqlSessionFactory 实例只能选择一种环境;
- 用ID这个唯一标识来切换默认使用的环境;
- MyBatis默认的事务管理器是JDBC,连接池是POOLED;
4.3、typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写;
使用场景:1、给实体类起别名
<typeAliases>
<typeAlias type="com.baidou.pojo.User" alias="user"/>
</typeAliases>
使用场景:2、扫描实体类的包,它的的默认别名为类名,首字母小写
<typeAliases>
<package name="com.baidou.pojo"/>
</typeAliases>
在实体类比较少的时候使用第一种方式,如果实体类比较多建议使用第二种方式!
区别:第一种可以自定义别名,第二种它就不行(如果非要改 需要在实体类上增加@Alias注解)。
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
4.4、settings(设置)
在MyBatis全局配置文件中通过<settings>
标签控制MyBatis全局开关。
MyBatis的设置:https://mybatis.org/mybatis-3/zh/configuration.html#settings
示例:在mybatis-config.xml配置日志输出
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
4.5、mappers(映射器)
mappers(映射器)的作用是告诉 MyBatis 到哪里去找映射文件;
使用场景:1. 使用相对于类路径的资源引用(推荐使用)
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
使用场景:2. 使用映射器接口实现类绑定注册
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
使用场景:3. 使用扫描包进行注入绑定
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
注:接口和它的配置文件必须同名,接口和它的配置文件必须在同一个包下;
4.6、其它配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- databaseIdProvider(数据库厂商标识)
5、使用Map传参以及模糊查询
5.1、使用Map传参
当我们的实体类或者数据库中的表,字段或者参数过多时,我们应当考虑使用Map!(优点:可以不用插入指定的参数,比较灵活)
mapper中添加如下方法:
//使用Map传参添加用户
int addUser2(Map<String,Object> map);
//使用Map传参修改用户信息
int updateUser2(Map<String,Object> map);
mapper映射器中:
<insert id="addUser2" parameterType="map">
insert into mybatis.user (name, pwd) values(#{userName},#{passWorld}) ;
</insert>
<update id="updateUser2" parameterType="map">
update mybatis.user set name=#{userName},pwd=#{password} where id=#{userId} ;
</update>
编写测试:
@Test
public void testGeneralMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("userName","钻石王老五");
map.put("passWorld","23333");
System.out.println(mapper.addUser2(map) > 0 ? "添加成功" : "添加失败");
// 增删改需要提交事务
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
@Test
public void testGeneralMap2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userId",4);
map.put("userName","小花");
map.put("password","123");
System.out.println(mapper.updateUser2(map) > 0 ? "修改成功" : "修改失败");
// 提交事务
sqlSession.commit();
sqlSession.close();
}
最终表数据如下:
5.2、模糊查询的使用
1、在SQL拼接中使用通配符
2、在Java代码执行的时候,传递通配符 % 内容 %
6、解决属性名与字段名不一致问题
1、数据库表中的字段:
2、拷贝之前的配置文件 mybatis-config.xml & db.properties
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>
<!--引入外部配置文件-->
<properties resource="db.properties"/>
<!--给实体类取别名起别名-->
<typeAliases>
<package name="com.baidou.pojo"/>
</typeAliases>
<!--用于切换开发环境-->
<environments default="development">
<environment id="development">
<!--采用JDBC事务管理-->
<transactionManager type="JDBC"/>
<!--配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url"
value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--映射器-->
<mappers>
<package name="com.baidou.mapper"/>
</mappers>
</configuration>
db.properties:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8
username=root
password=123456
3、向pom.xml加入资源过滤配置,解决UserMapper.xml绑定异常
<!--解决资源过滤问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4、编写MyBatis工具类:
public class MyBatisUtils {
private static SqlSessionFactory sessionFactory = null;
// 获取SqlSessionFactory实例
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession
* @return
*/
public static SqlSession getSqlSession() {
return sessionFactory.openSession();
}
}
5、编写实体类与数据库不一致的情况
public class User {
private Integer id;
private String name;
private String password; //对应数据表中的pwd字段
private char gender;
private String addr;
public User(Integer id, String name, String password, char gender, String addr) {
this.id = id;
this.name = name;
this.password = password;
this.gender = gender;
this.addr = addr;
}
public User(String name, String password, char gender, String addr) {
this.name = name;
this.password = password;
this.gender = gender;
this.addr = addr;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", gender=" + gender +
", addr='" + addr + '\'' +
'}';
}
}
6、编写UserMapper及UserMapper.xml并放到同一个包下
UserMapper接口:
public interface UserMapper {
// 通过id获取用户信息
User getUserById(int id);
}
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:绑定一个对应的Dao/Mapper接口 -->
<mapper namespace="com.baidou.mapper.UserMapper">
<!--
select语句
id:绑定对应方法
resultType:返回值类型
parameterType:参数类型
-->
<select id="getUserById" resultType="user" parameterType="int">
select *
from mybatis.user
where id = #{id};
</select>
</mapper>
7、编写测试
public class ApplicationDemo {
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
}
}
运行结果如下:
发现:这里的password的数据为null,是因为没有对应数据表中的字段pwd。
解决方案
方案1:修改sql起别名 (最暴力的解决方式)
select id,name,pwd as password,gender,addr
from mybatis.user
where id = #{id};
运行结果如下:
方案2:在映射文件中配置resultMap(结果集映射)
<!--
resultMap:自定义结果集映射规则,自定义某个JavaBean的封装规则。
id:唯一id,方便引用。
type:自定义规则的Java类。
-->
<resultMap id="userMap" type="User">
<!--
column:数据库中的字段
property:实体类中的属性
-->
<result column="pwd" property="password"/>
</resultMap>
<!-- select语句 -->
<select id="getUserById" resultMap="userMap">
select *
from mybatis.user
where id = #{id};
</select>
运行结果如下:
7、日志的使用
7.1、配置日志工厂
- 排查数据库出现的异常以及打印SQL的执行流程
- 之前我们使用控制台打印和debug的方式,而现在我们使用MyBatis的日志工厂。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J 、LOG4J、LOG4J2、 JDK_LOGGING 、COMMONS_LOGGING 、STDOUT_LOGGING 、NO_LOGGING | 未设置 |
常用的有:
- LOG4J (它要指定一个配置文件)
- STDOUT_LOGGING (标准日志输出)
在mybatis核心配置文件上配置日志(这里使用STDOUT_LOGGING):
- 坐标在configuration属性的第二个位置。
7.2、配置LOG4J
(1) 导入log4j包
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
(2) 创建log4j.properties文件,并编写日志配置
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/MyTest.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
(3) 在mybatis-config.xml文件中配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
(4) 测试
8、分页的实现
8.1、使用limit分页
使用分页的好处:方便用户检索内容,减少数据库开销;
SQL语法如下:
select * from user limit startIndex,pageSize;
# 示例:查询数据表中前两条数据
select * from user limit 0,2; # [0,2]
(1) 编写Mapper接口中的方法,参数为map
// 分页查询用户
List<User> selectUser(Map<String,Integer> map);
(2) 编写SQL映射文件
<!-- 分页查询 -->
<select id="selectUser" resultType="user" parameterType="map">
select *
from mybatis.user
limit #{startIndex},#{pageSize};
</select>
(3) 在测试类中传参
分析:起始位置 = (当前页面 - 1 )* 页面大小;
@Test
public void testLimit() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int currentPage = 1; // 第几页
int pageSize = 2; // 每页显示几个
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex", (currentPage - 1) * pageSize);
map.put("pageSize", pageSize);
List<User> users = mapper.selectUser(map);
users.forEach(System.out::println);
sqlSession.close();
}
测试结果:
8.2、使用数组分页
分析:查询出全部数据存list,然后在list中截取需要的部分;
(1) 编写mapper接口中的方法
// 使用数组分页
List<User> selectUserByArray();
(2) 编写SQL映射文件
<select id="selectUserByArray" resultType="user">
select *
from mybatis.user
</select>
(3)编写测试
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int currentPage = 1; // 第几页
int pageSize = 2; // 每页显示几个
List<User> userList = mapper.selectUserByArray();
// (currentPage - 1) * pageSize 从第几条开始
// currentPage * pageSize 到第几条结束
List<User> users = userList.subList((currentPage - 1) * pageSize, currentPage * pageSize);
users.forEach(System.out::println);
sqlSession.close();
测试结果:
8.3、RowBounds分页
使用 RowBounds 进行分页,非常方便,不需要在 sql 语句中写 limit 即可完成分页功能。
(1) 编写mapper接口中的方法
// 使用RowBounds 分页
List<User> selectUserByRowBounds();
(2) 编写SQL映射文件
<select id="selectUserByRowBounds" resultType="user">
select *
from mybatis.user
</select>
(3) 编写测试
@Test
public void selectUserByRowBounds() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
int currentPage = 2; // 第几页
int pageSize = 2; // 每页显示几个
RowBounds rowBounds = new RowBounds((currentPage - 1) * pageSize, pageSize);
// 通过session.**方法进行传递rowBounds,[此方式现在已经不推荐使用了]
List<User> users = sqlSession.selectList(
"com.baidou.mapper.UserMapper.selectUserByRowBounds", null, rowBounds);
users.forEach(System.out::println);
sqlSession.close();
}
测试结果:
8.4、PageHelper
官方文档:https://pagehelper.github.io/
9、使用注解开发
MyBatis 也可以使用注解的方式开发,这样我们就可以减少编写 Mapper 映射文件;
MyBatis常用注解如下:
@Insert:实现添加
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
1、编写实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
private char gender;
private String addr;
public User(String name, String pwd, char gender, String addr) {
this.name = name;
this.pwd = pwd;
this.gender = gender;
this.addr = addr;
}
}
2、编写接口
public interface UserMapper {
// 查询全部用户
@Select("select * from user")
List<User> getUserAll();
// 通过id查询用户,用注解传参
// 当方法中存在多个参数时,参数的前面需要加 @Param("xxx") 注解,使用这个注解可以给参数起别名
@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int id);
// 添加用户
@Insert("insert into user(name,pwd,gender,addr) values(#{name},#{pwd},#{gender},#{addr})")
int addUser(User user);
// 修改用户
@Update("update user set name=#{name},pwd=#{pwd},gender=#{gender},addr=#{addr} where id=#{id}")
int updateUser(User user);
// 删除用户
@Delete("delete from user where id=#{uid}")
int deleteUser(@Param("uid") int id);
}
3、编写MyBatis核心配置文件
<!--使用映射器接口实现类绑定注册-->
<?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>
<!--引入数据库配置文件-->
<properties resource="db.properties"/>
<!--配置标准日志输出-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--给实体类起别名-->
<typeAliases>
<package name="com.baidou.pojo"/>
</typeAliases>
<!--开发环境,之后这些东西在Spring中配置-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--使用class绑定方式-->
<mappers>
<mapper class="com.baidou.mapper.UserMapper"/>
</mappers>
</configuration>
4、编写工具类
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
// 1、加载MyBatis配置文件,获取sqlSessionFactory
static {
try {
String resouce = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resouce);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过sqlSessionFactory构建SqlSession
*
* @return SqlSession
*/
public static SqlSession getSqlSession(boolean flag) {
return sqlSessionFactory.openSession(flag);
}
/**
* 增删改需要提交事务
* sqlSessionFactory.openSession(true);
*
* @return
*/
public static SqlSession getSqlSession() {
return getSqlSession(true); //自动提交事务
}
}
5、编写测试
public class MyBatisTest {
@Test
public void testGetUserAll() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUserAll();
users.forEach(System.out::println);
sqlSession.close();
}
@Test
public void testGetUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user); //User(id=1, name=张三, pwd=123456, gender=男, addr=北京)
sqlSession.close();
}
@Test
public void testAddUser() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.addUser(new User("布鲁克", "buluke123", '男', "复仇者联盟"));
if (i > 0) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
sqlSession.close();
}
@Test
public void testUpdateUser() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.updateUser(new User(1,"陈平", "123213", '男', "郑州"));
if (i > 0) {
System.out.println("修改成功");
} else {
System.out.println("修改失败");
}
sqlSession.close();
}
@Test
public void testDeleteUser() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUser(9);
if (i > 0) {
System.out.println("删除成功");
} else {
System.out.println("删除失败");
}
sqlSession.close();
}
}
@Param()注解的使用
- 当方法中有很多基本类型或String类型的参数,参数前面需要加@Param()注解;引用类型参数不用加;
- 如果方法中只有一个基本类型的参数,可以忽略@Param()注解,但是建议加上它;
- 我们在SQL中引用的#{xxx},就是我们在注解@Param(“xxx”)中设置的属性名;
#{} 和 ${}区别
- #{}是预编译处理,$ {} 是字符串替换。
- Mybatis在处理#{}时,会将sql中的#{}替换为占位符?,调用PreparedStatement的set方法来赋值;
- Mybatis在处理$ {}时,就是把${}替换成变量的值。
- 使用#{}可以有效的防止SQL注入,提高系统安全性;
10、MyBatis关联查询
表与表之间的关系:
- 一对一
- 一对多
- 多对多
10.1、一对一关联查询
分析:一个用户可以有多个订单,一个订单对应它所属的用户
数据库设计:
-- 用户表
CREATE TABLE `user`(
`id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
`username` VARCHAR(50) COMMENT '用户名',
`password` VARCHAR(50) COMMENT '密码',
`birthday` VARCHAR(50) COMMENT '生日'
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO user(username,password,birthday) VALUES('王富贵','123123','2000-1-1');
INSERT INTO user(username,password,birthday) VALUES('小芳','234123','2001-5-1');
INSERT INTO user(username,password,birthday) VALUES('王狗蛋','223333','2000-6-17');
-- 订单表
CREATE TABLE `orders`(
`id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
`ordertime` VARCHAR(255) COMMENT '订单时间',
`total` DOUBLE COMMENT '合计',
`uid` int COMMENT '外键',
CONSTRAINT `FK_uid` FOREIGN KEY(`uid`) REFERENCES `user`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO orders(ordertime,total,uid) VALUES('2020-11-27 21:00:05',3000,1);
INSERT INTO orders(ordertime,total,uid) VALUES('2020-11-27 20:05:00',4000,1);
INSERT INTO orders(ordertime,total,uid) VALUES('2020-11-27 17:15:20',2000,2);
INSERT INTO orders(ordertime,total,uid) VALUES('2020-11-27 15:50:00',1000,3);
1、编写实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private int id;
private Date ordertime;
private double total;
// 当前订单属于哪个用户(用java面向对象方式实现外键)
private User user;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String username;
private String password;
private String birthday;
}
2、编写接口:
public interface OrderMapper {
List<Order> findAll();
}
3、编写mapper映射文件:
<?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.baidou.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<!--手动指定字段与实体属性的映射关系
column:表中的字段,为了方便查阅把id取别名为oid
property:实体类的属性
-->
<id column="id" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!-- 第一种写法封装user -->
<!--<result column="uid" property="user.id"></result>-->
<!--<result column="username" property="user.username"></result>-->
<!--<result column="password" property="user.password"></result>-->
<!--<result column="birthday" property="user.birthday"></result>-->
<!-- 第二种写法,嵌入结果映射
property:order实体中的属性名称
javaType:order实体中的属性类型
-->
<association property="user" javaType="user">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *
from mybatis.orders o,
mybatis.user u
where o.uid = u.id
</select>
</mapper>
4、拷贝之前的数据库配置文件、mybatis核心配置文件、mybatis工具类:
5、编写测试
@Test
public void test1(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orderList = mapper.findAll();
orderList.forEach(System.out::println);
sqlSession.close();
}
测试结果:
10.2、一对多关联查询
查询一个用户有多少个订单
1、编写实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String username;
private String password;
private String birthday;
// 描述当前用户有哪些订单
private List<Order> orderList;
}
2、编写接口和映射文件
public interface UserMapper {
List<User> findAllUser();
}
<?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.baidou.mapper.UserMapper">
<!--配置结果集映射-->
<resultMap id="userMap" type="user">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!-- 配置将订单信息封装到集合中
ofType:参数类型
-->
<collection property="orderList" ofType="order">
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>
<select id="findAllUser" resultMap="userMap">
select *, o.id oid
from mybatis.user u,
mybatis.orders o
where u.id = o.uid
</select>
</mapper>
3、编写测试
@Test
public void test2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAllUser();
userList.forEach(System.out::println);
sqlSession.close();
}
测试结果:
10.3、多对多关联查询
引入中间表
11、动态SQL
动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,我们通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL 恰好解决了这一问题,可以根据场景动态的构建查询。
动态 SQL 只有几个基本元素,与 JSTL 或 XML 文本处理器相似,十分简单明了,大量的判断都可以在 MyBatis 的映射 XML 文件里配置,以达到许多需要大量代码才能实现的功能(注解方式配置SQL可读性差)。
动态 SQL 减少了平时编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。
MyBatis 动态SQL 包括以下几种元素:
- if
- choose (when,otherwise)
- trim (where,set)
- foreach