一、什么是MyBatis?
mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为java 对象并返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作
二、环境搭建
mybatis的环境搭建
第一步:创建maven工程并导入坐标
第二步:创建实体类和dao的接口
第三步:创建Mybatis的主配置文件
SqlMapConifg.xml
第四步:创建映射配置文件
IUserDao.xml
注意事项:
1、 mybatis的映射配置文件位置必须和dao接口的包结构相同
2、映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
3、映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
1、导入依赖:
注意:Mybatis依赖不能导入springboot的mybatis,因为springboot的mybatis依赖读取数据源的位置是在application.properties配置文件。而现在我们是要手动加载Mybatis.xml配置文件中的数据源与其它参数属性,只需要引入mybatis依赖即可
<!-- 连接mysql-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- mybatis框架-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- 日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!-- 单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
2、创建实体类User(略)
3、创建接口IUserMapper:(请注意:接口名称和Mapper.xml文件名称最好相同)
package com.itheima.day02_eesy01_mybatiscrud.dao;
import com.itheima.day02_eesy01_mybatiscrud.domain.QueryVo;
import com.itheima.day02_eesy01_mybatiscrud.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author Huangyt
* @version 1.0
* @date 2020/5/12 17:13
* 用户持久层接口
*/
public interface IUserMapper {
List<User> findAll();
public void saveUser(User user);
public void updateUser(User user);
public void deleteUser(int id);
public User findById(Integer id);
//模糊查询
public List<User> findByName(String username);
//查询总数
public int findTotal();
//根据queryVo中的条件查询用户(多条件查询写法一)
public List<User> findUserByVo(QueryVo vo);
//根据多个参数条件查询用户(多条件查询写法二, 此写法在Mapper.xml中不用指定parameterType)
public List<User> findUserByNameAndAddress(@Param("username") String username, @Param("address") String address);
}
4、创建SqlMapConfig.xml文件,配置Mybatis环境(事务、数据库连接用户名&密码、Mapper文件)
注意:SqlMapConfig.xml配置文件和application.properties(yml)的 url 配置格式不一样
application.properties(yml)的 url 配置格式
jdbc:mysql://localhost:3306/abc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
SqlMapConfig.xml的 url 配置格式(将连接符 & 替换成 amp;)
<property name="url" value="jdbc:mysql://localhost:3306/abc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<?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">
<!--mybatis主配置文件-->
<configuration>
<!-- 配置连接数据库的property-->
<!-- 可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息-->
<!-- resource属性: 用于指定配置文件的文职,是按照类路径的写法来写,并且必须存在于类路径下-->
<!-- <properties resource="jdbcConfig.properties">-->
<!-- </properties>-->
<!-- 设置执行打印日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!-- 使用typeAliases配置别名,它只能配置返回结果实体类的别名。指定别名后,别名将不区分大小写-->
<typeAliases>
<!-- <typeAlias type="com.itheima.day02_eesy01_mybatiscrud.domain.User" alias="User"></typeAlias>-->
<!-- 指定要配置别名的包,指定包所有的实体类自动注册别名,别名即使类名,也不区分大小写(保持全路径类名也是可以的)-->
<package name="com.itheima.day02_eesy01_mybatiscrud.domain"/>
</typeAliases>
<!-- 配置环境-->
<environments default="mysql">
<!-- 配置mysql环境-->
<environment id="mysql">
<!-- 配置事务类型:提交、回滚-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!-- 连接数据库的4个基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/abc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件的位置-->
<mappers>
<!-- <mapper resource="com/itheima/day02_eesy01_mybatiscrud/dao/IUserMapper.xml"></mapper>-->
<package name="com.itheima.day02_eesy01_mybatiscrud.dao"/>
</mappers>
</configuration>
5、创建对应UserDao接口的IUserMapper.xml配置文件:(请注意:接口名称和Mapper.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配置文件给哪个接口调用-->
<!--resultType指定封装返回数据的容器类型-->
<mapper namespace="com.itheima.day02_eesy01_mybatiscrud.dao.IUserMapper">
<!-- 配置查询结果的列名和实体类的属性名的对应关系(适用于从数据库查出来的表列名与实体类的属性名不一致的情况)-->
<resultMap id="userMap" type="com.itheima.day02_eesy01_mybatiscrud.domain.User">
<!-- 主键字段的对应-->
<id property="id" column="id"></id>
<!-- 非主键字段的对应-->
<result property="username" column="name"></result>
<result property="address" column="name"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</resultMap>
<select id="findAll" resultType="User">
select * from user;
</select>
<!-- parameterType 参数类型-->
<insert id="saveUser" parameterType="com.itheima.day02_eesy01_mybatiscrud.domain.User">
<!-- 配置插入操作后获取插入数据的ID-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into user (username,address,sex,birthday) values (#{username}, #{address}, #{sex}, #{birthday})
</insert>
<update id="updateUser" parameterType="com.itheima.day02_eesy01_mybatiscrud.domain.User">
update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id=#{id}
</update>
<!-- 当方法参数只有一个的时候 id = #{id} 中的 #{id} 可以起任意名称-->
<!-- parameterType可以写Integer、int、INT、java.lang.Integer-->
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="findById" parameterType="Integer" resultType="User">
select * from user where id = #{id}
</select>
<select id="findByName" parameterType="String" resultType="com.itheima.day02_eesy01_mybatiscrud.domain.User">
select * from user where username like "%"#{username}"%"
</select>
<select id="findTotal" resultType="Integer">
select count(id) from user
</select>
<!-- 根据QueryVo条件查询用户-->
<select id="findUserByVo" parameterType="com.itheima.day02_eesy01_mybatiscrud.domain.QueryVo" resultType="com.itheima.day02_eesy01_mybatiscrud.domain.User">
select * from user where username like "%"#{user.username}"%"
</select>
<!-- 使用resultMap定义的结构来封装查询结果-->
<select id="findUserByNameAndAddress" resultMap="userMap">
select * from user where username like "%"#{username}"%" and address = #{address}
</select>
</mapper>
6、测试:
@SpringBootTest
class Day02Eesy01MybatiscrudApplicationTests {
private InputStream resourceAsStream;
private SqlSession sqlSession;
private IUserMapper mapper;
@BeforeEach //用于在测试方法执行之前执行
public void init() throws IOException {
System.out.println("执行init");
//1、读取配置文件,生成字节输入流
resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");
//2、获取SqlSessionFactory对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
//3、获取SqlSession对象
sqlSession = sqlSessionFactory.openSession();
//4、获取Dao的代理对象
mapper = sqlSession.getMapper(IUserMapper.class);
}
@AfterEach//用于在测试方法执行之后执行
public void destroy() throws IOException {
System.out.println("执行destroy");
//使用sqlSession进行事务提交
sqlSession.commit();
//释放资源
sqlSession.close();
resourceAsStream.close();
}
@Test
void contextLoads() throws IOException {
List<User> all = mapper.findAll();
for(User user : all){
System.out.println(user);
}
}
}
执行MyBatis过程使用Mapper.xml方式和注解方式的不同点:
1、Mapper.xml方式:
扫描Mybatis.xml的mappers标签时找到resource属性然后解析Mapper.xml获取namespace、SQL语句、
方法名id,将多个< Select >标签语句的属性封装成Map< String, Mapper>
2、注解方式:
扫描Mybatis.xml的mappers标签时找到class属性然后解析Dao接口,扫描是否有@Select注解并获取其
中的SQL语句,再利用反射获取返回结果类型,方法名,该类的全名包路径,封装成Map< String,
Mapper>
共通点:
Mapper.xml方式和注解方式的共同作用就是获取方法的SQL语句和方法名,再将其封装成Map。后面的
获取Dao接口代理,利用动态代理机制,动态获取到代理类调用的方法名和参数,利用方法名获取SQL语句和返回类型信息,最后执行SQL语句的过程(原生JDBC+反射封装返回结果)都是一模一样的!
执行MyBatis过程使用Mapper.xml方式和注解方式的图解:
我们可以概括一下当Mybatis框架使用Mapper.xml方式或注解方式执行SQL语句的过程:
1、通过 Resources.getResourceAsStream(“SqlMapConfig.xml”) 扫描Mybatis配置文件信息(包括连接数据库的4个属性 driver、url、username、password,还有mappers标签映射的×××Mapper.xml配置文件中的namespace、方法id、SQL语句等,把以上所有信息提取并封装到Configuration实体类中
(换一句话说,当我们扫描完SqlMapConfig.xml配置文件和×××Mapper.xml配置文件,成功将信息封装到Configuration实体类后,这两个配置文件已经没有用了。后面所要调用的信息全部在Configuration实体类中调用即可)
2、把封装好的Configuration实体类传送给FactoryBuilder , FactoryBuilder 通过 build 方法又把Configuration实体类传送给SqlSessionFactory类,SqlSessionFactory类通过 openSession() 方法又把Configuration实体类传送给sqlSession类去处理。(击鼓传花,最终还是sqlSession类起主导作用呀)
3、UserDao mapper = sqlSession.getMapper(UserDao.class); 将传入的UserDao.class接口作为类加载容器,返回该代理对象。
4、执行 List all = mapper.findAll(); 时,mapper自动调用 invoke 方法获取该方法的名称和该方法所在类,即Dao接口的类路径,将以上两个字段合并形成key,然后获取Configuration实体类Map中的Mappers的SQL语句和返回结果类型信息,最后调用反射+JDBC技术实现查询数据库。
实现方法的过程:通过代理对象获取Key + JDBC + 反射获取类字段封装