前言
本文章为学习文章,根据尚硅谷的SSM框架学习视频所记录学习的文章
MyBatis历史
MyBatis最初是Apache的一个开源项目iBatis,2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码与2013年11月迁移到Github
iBatis一词来源于"internet"和"abatis"的组合,是一个基于Java的持久层框架.IBatis提供的持久层框架包括SQL Map和Data Access Objects(DAO)
MyBatis特性
- MyBatis是支持定制化SQL、存储过程以及高级映射的优秀持久层框架
- MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java 的POJO(Plain Old Java Objects,普通的Java对象 )映射成数据库中的记录
- MyBatis是一个半自动的ORM(Object Relation Mapping)框架
https://github.com/mybatis/mybatis-3/releases
和其他持久化层技术对比
- JDBC
-
- SQL夹杂在Java代码中耦合度高,导致硬编码内伤
-
- 维护不易且实际开发需求中SQL有变化、频繁修改的情况多见
-
- 代码冗长,开发效率低
- Hibernate和JPA
-
- 操作简便,开发效率高
-
- 程序中的长难复杂SQL需要绕过框架
-
- 内部自动生成SQL,不容易做特殊优化
-
- 基于全映射的全自动框架,大量字段和POJO进行部分映射时比较困难
-
- 反射操作太多,导致数据库性能下降
- MyBatis
-
- 轻量级,性能出色
-
- SQL和Java编码分开,功能边界清晰。Java代码专注业务,SQL语句专注数据
-
- 开发效率稍逊与HIbernate,但是完全能够接收
搭建MyBatis
开发环境
IDE:IntelliJ IDEA 2020.1.1 x64
构造工具:maven3.5.4
MyBatis版本:MyBatis3.5.4
创建一个Java-Maven工程
添加依赖
<dependencies>
mybatis
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
测试
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
sql的
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
创建MyBatis的核心配置文件
习惯上命名为mybatis-config.xml,这个文件名仅仅是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是src/main/resources目录下
<?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"><!-- 环境复数 下面是环境单数 -->
<!-- environments default-> environment id="development-->
<environment id="development">
<!-- 事务管理器,类型:JDBC -->
<transactionManager type="JDBC"/>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/><!-- 驱动类 -->
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/><!-- 连接地址 -->
<property name="username" value="root"/><!-- 用户名 -->
<property name="password" value="password"/><!-- 密码 -->
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="" />
</mappers>
</configuration>
创建mapper接口
MyBatis的mapper接口相当于以前的DAO,但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
package com.atguigu.mybatis.mapper;
public interface UserMapper {
int insertUser();
}
创建MyBatis映射文件
相关概念:ORM(Object Relationship Mapping)对象关系映射
- 对象:Java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
Java概念和数据库概念
类=表
属性=字段/列
对象=记录/行
①映射文件1命名规则
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
②MyBatis中可以面向接口操作数据,要保证两个一致:
a>mapper接口的全类目和映射文件的命名空间(namespace)保持一致
b>mapper接口的方法名和映射文件中编写SQL的标签的id属性保持一致
<?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="...">
<select id="..." resultType="...">
select*from...
</select>
</mapper>
命名空间对应Mapper接口全类名
<mapper namespace="com.atguigu.mybatis.mapper.UserMapper"></mapper>
语句的id要和类的方法名一致
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</insert>
一定要将配置文件引入到MyBatis核心配置文件中
<mapper resource="mappers/UserMapper" />
核心配置文件和resource文件同一级所以,如果引用的配置文件有父目录,直接父目录/UserMapper
<?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.atguigu.mybatis.mapper.UserMapper">
<!--
mapper接口和映射文件要保证两个一致
1.mapper接口的全类名和映射文件的namespace一致
2.mapper接口中的方法名要和映射文件中的sql的id保持一致
-->
<!-- int insertUser(); -->
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</insert>
</mapper>
过程解析
首先创建POJO类对应数据库的表
创建MyBatis核心配置文件
创建Mapper接口
int insertUser()
创建XML文件,对应我们的Mapper接口
<mapper namespace="com.atguigu.mybatis.mapper.UserMapper">
<!--
mapper接口和映射文件要保证两个一致
1.mapper接口的全类名和映射文件的namespace一致
2.mapper接口中的方法名要和映射文件中的sql的id保持一致
-->
<!-- int insertUser(); -->
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</insert>
然后在核心配置文件中引入该XML文件
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper" />
</mappers>
在测试类中首先获取XML文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
获取SQL会话工厂构建对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
获取SQL会话工厂对象
通过build方法创建
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
调用SQL会话工厂对象的openSession()方法来获取SQL会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
通过该方法的getMapper来创建一个类的对象,此处包括了接口
底层会生成一个该接口的实现类,我们直接调用接口的方法即可
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
调用方法并且获取返回值打印
int result = mapper.insertUser();
因为该操作的事务需要手动管理,所以不会提交,为了避免回滚我们需要手动提交一下
sqlSession.commit();
释放会话流资源
sqlSession.close();
空参sqlSessionFactory.openSession();不会自动提交事务
单参sqlSessionFactory.openSession(true);会自动提交事务
优化功能
添加log4j依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
源码验证和日志级别
执行原理
通过反射来获取参数然后封装执行
日志的级别
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细
标题
修改和删除用户的功能
封装一个SqlSession对象工厂创建工具类
package com.atguigu.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class SqlsessionUtil {
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
try {
//获取核心配置文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//获取SqlSession对象 填入true 默认事务提交
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException ioException) {
ioException.printStackTrace();
}
return sqlSession;
}
}
首先在UserMapper接口中写入要增加的方法
void updateUser();
void deleteUser();
在MyBatisTest测试类中调用
//获取对象
SqlSession sqlSession = SqlsessionUtil.getSqlSession();
//创建代理
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser();//调用方法
在XML文件配置中添加配置信息如下
<update id="updateUser">
update t_user set username = 'root',password = '123' where id = 3
</update>
<delete id="deleteUser">
delete from t_user where id = 3;
</delete>
测试查询功能
查询一定要在配置文件中指明结果映射,此处需要指明结果映射类型
<!--
resultType:设置结果类型,即查询的数据要转换为的Java类型
resultMap:自定义映射,处理多对一或一对多的映射关系
-->
<select id="getUserById" resultType="com.atguigu.mybatis.pojo.User">
select*from t_user where id = 1
</select>
List<User> getAllUser();
<!-- List<User> getAllUser(); -->
<select id="getAllUser" resultType="com.atguigu.mybatis.pojo.User">
select*from t_user;
</select>
核心配置文件-environments
环境标签
environments:配置数据库连接环境
属性:
default:设置默认使用的环境的id
environment:设置一个具体的连接数据库的环境
属性:
id:设置环境的唯一标识,不能重复
transactionManager:事务管理器
dataSource:数据源
<?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文件,此后就可以在当前文件中来使用${key}的方式来访问value -->
<properties resource="jdbc.properties"/>
<!--
environments:配置连接数据库的环境
属性:
default:设置默认使用的环境id
-->
<environments default="development">
<!-- environment:设置一个具体的连接数据库的环境
属性:
id:设置环境的唯一标识,不能重复
-->
<environment id="development">
<!-- transactionManager:设置事务管理器
属性:
type:设置事务管理的方式
type="JDBC/MANAGED"
JDBC:表示使用JDBC原生的事务管理方式
MANAGED:被管理,例如Spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource:设置数据源
属性:
type:设置数据源的类型
type="POOLED/UNPOOLED/JNDI"
POOLED:表示使用数据库连接池
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper" />
</mappers>
</configuration>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=password
核心文件配置-typeAliases
typeAliases类型别名
<?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>
<!--
MyBatis核心配置文件中的标签必须要按照指定的顺序配置:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?
-->
<!-- 引入properties文件,此后就可以在当前文件中来使用${key}的方式来访问value -->
<properties resource="jdbc.properties"/>
<!--
typeAliases:设置类型别名,即为某个具体的类型设置一个别名
在MyBatis的范围中,就可以使用别名来表示一个具体的类型
-->
<typeAliases>
<!--
type:设置起别名的类型
alias:设置某个类型的别名
-->
<!--<typeAlias type="com.atguigu.mybatis.pojo.User" alias="abc"></typeAlias>-->
<!-- 若不设置alias,当前的类型拥有默认的别名,即类名且不区分大小写 -->
<!--<typeAlias type="com.atguigu.mybatis.pojo.User"></typeAlias>-->
<!-- 通过包设置类型别名,指定包下所有的类型将全部拥有默认的别名 -->
<package name="com.at guigu.mybatis.pojo"/><!-- 直接设置包路径 -->
</typeAliases><!-- 类型别名 -->
<!--
environments:配置连接数据库的环境
属性:
default:设置默认使用的环境id
-->
<environments default="development">
<!-- environment:设置一个具体的连接数据库的环境
属性:
id:设置环境的唯一标识,不能重复
-->
<environment id="development">
<!-- transactionManager:设置事务管理器
属性:
type:设置事务管理的方式
type="JDBC/MANAGED"
JDBC:表示使用JDBC原生的事务管理方式
MANAGED:被管理,例如Spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource:设置数据源
属性:
type:设置数据源的类型
type="POOLED/UNPOOLED/JNDI"
POOLED:表示使用数据库连接池
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper" />
</mappers>
</configuration>
核心文件配置-mappers
创建包目录 com/atguigu/mybatis/mapper
我们为了方便mappers标签的引入,将创建和包相匹配的目录
<mappers>
<package name="com.atguigu.mybatis.mapper"/>
</mappers>
创建核心配置文件和映射文件模板
自定义模板保存到IDEA中,创建文件时直接使用模板
mybatis-config
<?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>
<!--
MyBatis核心配置文件中的标签必须要按照指定的顺序配置:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?
-->
<properties resource="jdbc.properties"/>
<typeAliases>
<package name=""/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name=""/>
</mappers>
</configuration>
mybatis-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="">
</mapper>
Mybatis获取参数的两种方式
/*
Mybatis获取参数值的两种方式:#{}和${}
#{}的本质是占位符赋值,${}的本质是字符串拼接
1、若mapper接口方法的参数为单个的字面量类型
此时可以通过#{}和${}以任意的内容获取参数值,一定要注意${}的单引号问题
2、若mapper接口方法的参数为多个字面量类型
此时MyBatis会将参数放在map集合中,以两种方式来存储数据
a>以arg0,arg1,arg2...
b>以param1,param2,param3...
因此只需要通过#{}和${}来访问map集合的键,就可以来获取相对应的值
一定要注意${}的单引号问题
3、若mapper接口的方法参数为一个map集合类型的参数
只需要通过#{}和${}来访问map集合的键,就可以来获取相对应的值
4、若mapper接口的方法参数为实体类类型的参数
只需要通过#{}和${}来访问实体类中的属性名,就可以获取相对应的属性值
5、可以在mapper接口方法的参数上设置@Param注解
此时将MyBatis会将这些参数放在map中,以两种方式进行存储
a>以@Param注解的value属性值为键,以参数值
b>以param1,param2...为键,以参数为值
只需要通过#{}和${}来访问实体类中的属性名,就可以获取相对应的属性值
*/
${}:拼接字符串形式
#{}:占位符形式
<!-- select * from t_user where username=#{username}-->
select*from t_user where username = '${username}'<!-- 无法自动解析字符串 -->
也可以自定义键
SqlSession sqlSession = SqlsessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("username","admin");
map.put("password","123456");
User user = mapper.checkLoginByMap(map);
System.out.println("user = " + user);
SqlSession sqlSession = SqlsessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(null,"root","123456",33,"女","123@qq.com");
mapper.insertUser(user);
<!-- User checkLoginByMap(Map<String,Object>map); -->
<select id="checkLoginByMap" resultType="User">
select * from t_user where username = #{username} and password = #{password}
</select>
<!-- void insertUser(User user); -->
<select id="insertUser">
insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
</select>
@Test
public void testGetAllUserToMap(){
SqlSession sqlSession = SqlsessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
// List<Map<String, Object>> allUserToMap = mapper.getAllUserToMap();
Map<String, Object> map = mapper.getAllUserToMap();
// System.out.println("allUserToMap = " + allUserToMap);
System.out.println("map = " + map);
}
/**
* 查询所有的用户信息为map集合
* @return
*/
//List<Map<String,Object>>getAllUserToMap();
@MapKey("id")
Map<String,Object>getAllUserToMap();
若查询的数据有多条时,并且要将每条数据转换为我们的Map集合,此时有两种解决方案:
1、将Mapper接口方法的返回值设置为泛型是Map的List集合
2、可以将每条数据转换成的Map集合放在一个大的Map集合中,但是必须要通过@MapKey注解,来讲查询的某个字段的值作为大的Map的键 此处为@MapKey(“id”)
特殊的SQL执行
如果我们想要进行一个模糊查询,则普通的SQL语句无法完成该操作
try {
Class.forName("");
Connection connection = DriverManager.getConnection("","","");
String sql = "select*from t_user where username like '%?%'";
PreparedStatement ps = connection.prepareStatement(sql);
//ps.setString(1,"a");
} catch (Exception e) {
e.printStackTrace();
}
String sql = "select*from t_user where username like '%?%'";
这句代码我们在IDEA中
如果填充占位符会直接编译报错
因为此处的占位符被当成了字符串的一部分
所以我们的Mapper映射文件也是如此
<!-- List<User> getUserByLike(@Param("mohu") String mohu); -->
<select id="getUserByLike" resultType="User">
<!-- 此处的like '%#{mohu}%' 执行时将会编程 '?' 没有填充占位符-->
select*from t_user where username like '%#{mohu}%';
</select>
解决方案:三种
第一种
第一种解决方法将占位符直接写死
<select id="getUserByLike" resultType="User">
select*from t_user where username like '%a%';
</select>
第二种
<!--使用函数类型-->
<select id="getUserByLike" resultType="User">
select*from t_user where username like concat('%'#{mohu}m,'%');
</select>
第三种
<!-- 使用字符串拼接 -->
<select id="getUserByLike" resultType="User">
select * from t_user where username like "%"#{mohu}"%"
</select>
批量查询
此处要进行批量操作,但是如果仅仅是#{ids}
拼接时将会携带’',变成字符串形式,导致报错,此处需要使用${}形式来进行SQL语句执行
<!-- void deleteMoreUser(@Param("ids") String ids); -->
<!-- SqlSession sqlSession = SqlsessionUtil.getSqlSession();
SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
mapper.deleteMoreUser("9,10"); -->
<!-- void deleteMoreUser(@Param("ids")String ids); -->
<select id="deleteMoreUser">
delete from t_user where id in(${ids});
</select>
动态设置表名
我们将表名设置为一个动态的参数,通过动态参数来赋值给SQL语句来达到动态的查询方式
<!-- List<User>getUserList(@Param("tableName") String tableName); -->
<select id="getUserList" resultType="User">
该方式会将tableName变成占位符
select*from #{tableName}
下面的形式才是正确的拼接方式
select*from ${tableName}
</select>
获取自增列的值
在原生的JDBC中如果想要调用的话需要打开属性,输入1或者常量
<!-- void insertUser(User user);//实体类不需要设置Param 直接属性名 -->
<!-- useGeneratedKeys表示使用主键 -->
<!-- keyProperty表示赋值的属性 id -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id"><!-- 使用自增的主键 -->
insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email});
</insert>