一、MyBatis初次使用
2.1 环境搭建步骤
MyBatis 的 API : https://mybatis.org/mybatis-3/zh/getting-started.html
1.引入依赖包

2.准备核心配置件

db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://123.57.206.19:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root
password=123456
mybatis.xml
在resources下定义MyBatis的配置文件,无固定名,但大部分人使用 resources/mybatis.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"></properties>
<!--    <settings>-->
<!--        <setting name="logImpl" value="LOG4J"/>-->
<!--    </settings>-->
    <typeAliases>
        <!--给单个类起别名。 type:类型 alias:别名-->
        <typeAlias type="com.test.pojo.Student" alias="student"></typeAlias>
        <!--给指定包下所有类起别名。 别名=类名(不区分大小写)-->
        <package name="com.test.pojo"/>
    </typeAliases>
    <!--    配置mybaits中数据库连接环境-->
    <environments default="mysql">
        <environment id="mysql">
            <!--配置myabtis中事务 和 JDBC 保持一致-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接数据库的4个元素, 底层采用的是数据库连接池的方式-->
            <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>
    <!--扫描mapper文件-->
    <mappers>
        <mapper resource="mapper/student.xml"></mapper>
    </mappers>
</configuration>
3.书写mapper文件
resources/**.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: 代表xml的名称,类似java包名-->
<mapper namespace="com.beiyou.dao.StudentDao">
   <!-- 查询所有学生 List<Student> selectAll()-->
    <!--
      select: 代表进行查询操作。
          id: 之前的方法名称,具有唯一性。
   resultType: 返回值类型。
               如果返回的是对象,直接书写对象类型的的完整名。
               如果是集合,书写的是集合的泛型
 parameterType: 参数类型,可以省略。
   -->
    <select id="selectAll" resultType="com.beiyou.entity.Student"  >
       select * from student
    </select>
</mapper>非必需
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory><!--所在的目录-->
            <includes>
                <!--.xml 文件都会扫描到,包括子目录-->
                <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.构建SqlSessionFactory。
从xml中创建SqlSessionFactory.
// 1. 解析扫码 mybatis.xml 文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
// 2. 获取sqlsession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 获得 sqlsession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 执行sql语句
List<Student> students = sqlSession.selectList("com.beiyou.dao.StudentDao.selectAll");
// 打印结果
System.out.println(students);
是否弥补了JDBC的不足?
二、MyBatis 配置细节
2.1 log4j的使用
-  加入依赖 
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>-  配置文件 log4j.properties 
#定义全局日志级别调试阶段推荐debug
log4j.rootLogger = error,stdout
#包级别日志
log4j.logger.test.a = debug
### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.SimpleLayout
### 输出日志到文件=/logs/log.log ###
log4j.appender.logfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File = /logs/log.log
log4j.appender.logfile.layout = org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n2.2 事务配置
transactionManager.type
JDBC : 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED : 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
2.3 连接池配置
dataSource.type
 UNPOOLED : 这个数据源的实现会每次请求时打开和关闭连接.
 POOLED : 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
 JNDI : 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
2.4 映射文件的加载方式
1.resource: 使用相对于类路径的资源引用。
 <mapper resource="AuthorMapper.xml"/>2.url: 使用完全限定资源定位符(URL)
 <mapper url="file:///D:/207/mybatis/src/main/resources/mapper/BlogMapper.xml"/>3.class : 使用映射器接口实现类的完全限定类名
 <mapper class="org.mybatis.builder.BlogMapper"/>4.name : 将包内的映射器接口实现全部注册为映射器
<mappers>
  <package name="com.beuyou.dao"/>
</mappers>2.5 实体类别名处理
<typeAliases>
    <!--给单个类起别名。 type:类型 alias:别名-->
    <typeAlias type="com.beiyou.entity.Student" alias="student"></typeAlias>
    <!--给指定包下所有类起别名。 别名=类名(不区分大小写)-->
    <package name="com.beuyou.entity"/>
</typeAliases>常见的 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 | 
2.6 外部属性配置文件存储数据库信息
-  配置db.properties数据库信息 
driver=com.mysql.cj.jdbc.Driver
url=mysql://rm-bp169j3q9n43kxauzco.mysql.rds.aliyuncs.com:3306?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root123
password=Root_123<properties resource="db.properties"></properties>
 <dataSource type="POOLED">
       <property name="driver" value="${driver}"/>
       <property name="url" value="${url}"/>
       <property name="username" value="${username}"/>
       <property name="password" value="${password}"/>
 </dataSource>三、Mapper文件配置
3.1 常用属性

3.2 SQL 定义标签
1. select
用于数据查询操作,例:
<select id="selectUserInfo" parameterType="int" resultType="map">
  select * from user_info where id=#{keyId}
</select>
2. insert
用于数据保存操作,例:
<insert id="insertUserInfo" parameterType="map" useGeneratedKeys="true" keyProperty="keyId">
  insert into user_info (
	userName,
	userSex
  )values(
  	#{userName},
  	#{userSex}
  )
</insert>
PS:keyProperty属性可返回此条插入数据的主键值
3. update
用于数据更新操作,例:
<update id="updateUserInfo" parameterType="map">
  update  user_info
  set userName=#{userName}
  where id=#{keyId}
</update>
4. delete
用于数据删除操作,例:
<delete id="selectUserInfo" parameterType="int">
  delete  from user_info 
  where id=#{keyId}
</delete>
5. resultMap
SQL返回与实体类映射关系信息,例
<resultMap id="userInfoMap" type="User">
  <result property="user_name" column="userName"/>
  <result property="user_sex" column="userSex"/>
</resultMap>
<select id="selectUserInfo" parameterType="int" resultType="userInfoMap">
  select
  userName,
  userSex
  from user_info 
  where id=#{keyId}
</select>
将数据表字段userName、userSex映射到实体类User的user_name、user_sex
6. sql
用于定义可重用的 SQL 代码片段,以便在多个SQL语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。例:
<!-- 定义 -->
<sql id="userColumns"> ${alias}.userName,${alias}.userSex</sql>
<!-- 运用 -->
<select id="selectUserInfo" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from user_info  t1
  left join user_info_copy t2
</select>
3.3、SQL动态标签
1. if
单个条件判断,用以实现条件筛选,例:
<select id="selectUserInfo" parameterType="map" resultType="map">
  select * from user_info 
  where 1=1
  <if test="userSex !=null and userSex !='' ">
  	and userSex=#{userSex}
  </if>
  <if test="userName !=null and userName !='' ">
  	and userName like CONCAT('%',#{userName},'%')
  </if>
</select>
2. foreach
用于更新或保存数据时的批量操作,例:
<!-- userList为List<HashMap<String,Object>>类型数据 -->
insert into user_info(
userName,
userSex
)values
<foreach item="item" index="index" collection="userList" separator="," >
(
#{item.userName},
#{item.userSex}
)
</foreach>
<!-- userList为List<String>类型数据 -->
insert into user_info(
userName
)values
<foreach item="item" index="index" collection="userList" separator="," >
(
#{userName}
)
</foreach>
update user_info
set userAge=#{userAge}
where id in
<foreach collection="keyIds" index="index" item="item" separator="," open="(" close=")">
#{item}
</foreach>
3. choose/when/otherwise
用以实现条件的多种判断,类似与if else,例:
<select id="selectUserInfo" parameterType="map" resultType="map">
  select * from user_info 
  where 1=1
  <choose>
  	<when test="userFlag!=null and userFlag!='' and userFlag=='Y'">
  		and id<=100
  	</when>
  	<when test="userFlag!=null and userFlag!='' and userFlag=='N'">
  		and id <=200
  	</when>
  	<otherwise>
  		and id<=300
  	</otherwise>
  </choose>
</select>
4. where
只会在子元素返回任何内容的情况下才插入 “WHERE” 子句,并且可以自动处理判断条件语句返回的第一个and或or,例:
不使用where标签时,若userSex为空,语法错误会报错:
<select id="selectUserInfo" parameterType="map" resultType="map">
  select * from user_info 
  where
  <if test="userSex !=null and userSex !='' ">
  	userSex=#{userSex}
  </if>
  <if test="userName !=null and userName !='' ">
  	and userName like CONCAT('%',#{userName},'%')
  </if>
</select>
修改为:
<select id="selectUserInfo" parameterType="map" resultType="map">
  select * from user_info
  <where>
  <if test="userSex !=null and userSex !='' ">
  	userSex=#{userSex}
  </if>
  <if test="userName !=null and userName !='' ">
  	and userName like CONCAT('%',#{userName},'%')
  </if>
  </where>
</select>
自动转换为:select * from user_info where userName like ……
5. set
可以动态更新需要更新的列,忽略其它不更新的列,例:
<update id="updateUserInfo" parameterType="map">
  update  user_info
  <set>
  <if test="userName!= null and userName!=''">
  userName=#{userName},
  </if>
  userSex=#{userSex}
  </set>
  where id=#{keyId}
</update>
四、基于MyBatis的CURD操作
使用单元测试验证
  <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.6.0</version>
      <scope>test</scope>
    </dependency>
4.1 MyBatis查询的三种方式
-  返回单个对象 selectOne 
-  返回对象List集合 selectList 
-  返回对象Map集合 selectMap 
<select id="selectOne" resultType="student">
   select *  from student where id=1
</select>
<select id="selectAll" resultType="student"  >
   select * from student
</select>
<select id="selectMap" resultType="map">
   select *  from student
</select>4.2 MyBatis参数传递的三种方式
4.2.1 三种传参
-  传递的是基本类型+String ,使用 param1 
-  传递类型是对象,接受使用对象的 属性名 
-  传递的是map集合,接受时候使用map中的 key 
<!-- 方法 Student selectOne(int id)-->
<!--param1:-->
<select id="selectOne" resultType="student" parameterType="int">
    select *  from student where id = #{id}
</select>
<!-- 方法 Student selectOne(StudentQuery query)-->
<!--#{} 里面放的是对象属性-->
<select id="selectOne2" resultType="student">
    select *  from student where id = #{id} and name = #{name}
</select>
<!-- 方法 Student selectOne(Map map)-->
<!--#{} 里面放的是map的key-->
<select id="selectOne3" resultType="student">
    select *  from student where id = #{a} and name = #{b}
</select>// 【A】. 传递基本类型
 Student student = sqlSession.selectOne("test.c.selectOne", 2);
 // 打印
 System.out.println(student);
 // 【B】. 传递对象
 StudentQuery query = new StudentQuery();
 query.setId(2);
 query.setName("周星星");
 Student student = sqlSession.selectOne("test.c.selectOne2", query);
 //打印
 System.out.println(student);
 //【C】. 传递Map集合
 //id,name 一块封装到map集合
 Map<String,Object> map = new HashMap<>();
 map.put("a",2);
 map.put("b","周星星");
 Student student = sqlSession.selectOne("test.c.selectOne3", map);
 //打印
 System.out.println(student);4.2.2 #和$区别:面试题
#:底层相当于占位符?
$:底层相当于字符串拼接
-  两者相比,占位符的方式更加方便,可以有效的防止SQL注入。 
-  预编译 
4.2.3 模糊查询
<!-- 模糊查询 -->
<select id="selectOne4" resultType="student">
     Student student = sqlSession.selectOne("test.c.selectOne4", "%敏%");
     select *  from student where  name like #{param1}
     Student student = sqlSession.selectOne("test.c.selectOne4", "敏");
     select *  from student where name like concat('%',#{param1},'%')
</select>4.2.4 Model对象字段名称与数据库不一致,使用resultMap指定
 <select id="selectlike2" resultMap="usermap" >
        select *  from user where email like concat('%',#{param1},'%')
    </select>
    <resultMap id="usermap" type="User">
        <!--
            主键映射  使用id标签
            propetry java中的类型名称
            column  数据库中的字段名
        -->
        <id property="pwd" column="password"/>
    </resultMap>
XML
4.2.5 include标签
1、首先定义一个sql标签,一定要定义唯一id。    <sql id="columns">
        id, title ,brief
    </sql>
2、然后通过id引用 <select id="selectOne"  resultMap="productResultMap1" >
        select
        <include refid="columns"/>
        from product where id = 8
 </select>
XML
4.3 MyBatis完整DML全部操作
DML与DDL的含义:
1、DML(Data Manipulation Language)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言,简而言之就是实现了基本的“增删改查”操作。包括的关键字有:select、update、delete、insert、merge
2、DDL(Data Definition Language)数据定义语言-用于定义和管理 SQL 数据库中的所有对象的语言,对数据库中的某些对象(例如,database,table)进行管理。包括的关键字有:
create、alter、drop、truncate、comment、grant、revoke
4.3.1 CUD
【1】新增
<!-- 方法 int insert(Student student)-->
<insert id="insert">
    insert into student (name,age) values (#{name},#{age})
</insert>Student student = new Student();
student.setName("邓超");
student.setAge(38);
int rowNum  = sqlSession.insert("com.beiyou.dao.StudentMapper.insert", student);
//MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事务
sqlSession.commit();【2】修改
<!-- 传统方法 int update(Student student)-->
<update id="update">
    update student set name = #{name},age = #{age} where id = #{id}
</update>Student student = new Student();
student.setName("邓超111");
student.setAge(380);
student.setId(6);
int rowNum  = sqlSession.update("com.beiyou.dao.StudentMapper.update", student);
//MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事务
sqlSession.commit();【3】删除
<!-- 传统方法 int delete(int id)-->
<delete id="delete">
    delete from student  where id = #{param1}
</delete>int rowNum  = sqlSession.delete("test.d.delete", 6);
//MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事务
sqlSession.commit();4.3.2 设置SqlSession提交
MyBatis 默认不自动提交事务,所以 增删改功能 需要我们手动提交事
【1】SqlSession sqlSession = factory.openSession(true);
【2】sqlSession.commit();
4.4 扩展
4.4.1 接口编程
package com.beiyou.dao;
public interface UserDao {
    List<User> selectAll();
}
<mapper namespace="com.beiyou.dao.UserDao">
 <select id="selectAll" resultType="user" >
     select  *  from 202_user
 </select>
</mapper>
  
  UserDao mapper = sqlSession.getMapper(UserDao.class);
  mapper.selectAll();4.4.2通过表达式,实现多场景多条件组合查询
<select id="select" resultMap="productResultMap1">
        select id, categoryId,title ,brief from 202_product
        <where>
            <if test="id != null">
                and  id = #{id}
            </if>
            <if test="ids != null">
                and id in
                <foreach collection="ids" item="item"  open="(" close=")" separator=",">
                    #{item}
                </foreach>
            </if>
            <if test="categoryId != null">
                and  categoryId= #{categoryId}
            </if>
            <if test="categoryIds != null">
                and categoryId in
                <foreach collection="categoryIds" item="item" open="(" close=")" separator=",">
                    #{item}
                </foreach>
            </if>
            <if test="name != null">
                and title like concat('%',#{name},'%')
            </if>
        </where>
    </select>
    @Test
    public void selectQuery() throws IOException {
        ProductDao productDao = sqlSession.getMapper(ProductDao.class);
        ProductQuery query = new ProductQuery();
        //query.setId(40);
        //query.setCategoryId(1);
        //query.setName("梨38");
        //query.setIds(new Integer[]{38,42,50,51,52});
        query.setCategoryIds(new Integer[]{3});
        List<Product> products = productDao.select(query);
        System.out.println(products);
    }
4.4.3 注解
 <mapper class="com.beiyou.dao.UserDao"/>public interface UserDao {
    @Select("select *  from  202_user limit 1")
    User select();  //insert into order_item (productId,productName,productImg,price,qty,orderId)  values (1,2,3),(2,3,4).....
    @Insert("<script> " +
            "insert into "  +
            " order_item (productId,productName,productImg,price,qty,orderId) " +
            "values " +
            "<foreach collection='items'  item='item' separator=','> "+
            "(#{item.productId},#{item.productName},#{item.productImg},#{item.price},#{item.qty},#{item.orderId})"+
            "</foreach> </script>" )
      int insertAll(List<OrderItemEntity> items);
    @Select("<script> " +
            "select *  from  order_item where 1 = 1 "  +
            "<if test='id != null'>" +
            " and id = #{id} "+
            "</if> "+
            "<if test='orderId != null'>" +
            " and orderId = #{orderId} "+
            "</if> "+
            "<if test='orderIds != null'>" +
            " and orderId in  "+
            "<foreach collection='orderIds' open='(' close=')'  item='item' separator=','> "+
            "#{item}"+
            "</foreach> "+
            "</if> "+
           "</script>" )@Results(id="studentMap",value={    @Result(column=“id”, property=“id”, jdbcType=JdbcType.INTEGER, id=true),    @Result(column=“name”, property=“name”, jdbcType=JdbcType.VARCHAR),    @Result(column=“class_id”, property=“classId”, jdbcType=JdbcType.INTEGER)})
    List<OrderItemEntity> select(OrderItemQueryDto queryDto);
}
4.4.4 SelecKey标签使用
Mybatis之useGeneratedKeys和selectKey的基本用法与区别_mybatis selectkey usegeneratedkeys_poppyCL的博客-CSDN博客
一、useGeneratedKeys数据库本身具备主键自动增长的功能,才能使用useGeneratedKeysoracle不支持true<insert id="insert" useGeneratedKeys="true" keyProperty="idColName"> insert into tableName (colName) values (#{colVal,jdbc..._mybatis selectkey usegeneratedkeys
https://blog.csdn.net/poppyCL/article/details/103347385
   <insert id="insert"  parameterType="UserEntity">
        insert user (email,password) values (#{email},#{pwd})
        <selectKey keyProperty="id" resultType="integer" keyColumn="newId" order="AFTER">
            SELECT LAST_INSERT_ID() as newId
        </selectKey>
    </insert>
<selectKey resultType="integer" keyColumn="newId" keyProperty="id" order="BEFORE">
              SELECT (max(id)+1) as newId from  205_category
</selectKey>
注解版
@SelectKey(statement="SELECT last_insert_id", keyProperty="id", before=false, resultType=Long.class)
五 MyBatis 高级关系查询
-  一个会员只属于一个详情 ==> 会员对详情表是一对一关系 
-  不管是一对一还是多对多,都要使用<resultMap> ,属性有id 和type 
-  一对一中,<resultMap>内要用<association>来映射复杂对象,属性有 : 
-  (property和javaType) ==> 嵌套结果 
-  (property, column, select) ==> 嵌套查询 
-  一对多中,<resultMap>内要用<collection>来映射复杂对象,属性有property和ofType 
-  注意防范<resultMap>和<association>或<collection>中字段名冲突的问题! 
5.1 一对一
<resultMap> <association>
<association> 元素,通常可以配置一下属性
- propery:指定映射到实体类对象属性,与表字段一一对应
- column:指定表中对应的字段
- javaType:指定映射到实体对象属性的类型
- select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询
- fetchType:指定在关联查询时是否启用延迟加载。FetchType属性有lazy和eager(实时)两个属性值,默认值为lazy
默认为lazy(默认关联映射延迟加载)
create table 202_user(    id int unsigned auto_increment,    tel varchar(50) not null,    password varchar(32) not null,    primary key(id));
CREATE TABLE `202_userinfo` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '唯一标识',
  `name` varchar(100) NOT NULL COMMENT '姓名',
  `sex` varchar(100) DEFAULT NULL COMMENT '性别',
  PRIMARY KEY (`id`)
)
实体对象
@Data public class User {    private Integer id;    private String tel;    private String password;    private UserInfo userinfo;}public class UserInfo {    private Integer id;    private String name;    private String sex;   private Integer age;
}
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">
<mapper namespace="com.beiyou.dao.UserDao2">
    <resultMap id="usermap" type="com.beiyou.model.User">
        <id property="id" column="id"/>
        <result property="tel" column="tel"/>
        <result column="password" property="password"/>
        <association property="userInfo" javaType="com.beiyou.model.UserInfo">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="sex" column="sex"/>
        </association>
    </resultMap>
    <select id="one2one" resultMap="usermap" >
        select *
        from `202_user`   u
        left join `202_userinfo`  ui
        on u.id = ui.userId
    </select>
   <resultMap id="userMap" type="com.beiyou.model.User">
    <id column="id" property="id"/>
    <result column="tel" property="tel"/>
    <result column="password" property="password"/>
    <association property="userInfo" column="Id"  fetchType="lazy" javaType="com.beiyou.model.UserInfo"
     select="selectUserinfo">
        <id property="id" column="id"/>   bug 必须书写
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
    </association>
  
</resultMap>
    <select id="lazyone2one" resultMap="usermap2">
        select  *  from  202_user
    </select>
    <select id="selectName" resultType="com.beiyou.model.UserInfo">
        select * from 202_userinfo where userId = #{id}
    </select>
</mapper>
UserDao.java
public interface UserDao {
     User one2one(String name);
     User lazyone2one(String name);
}
5.2 一对多
<resultMap> <collection>
CREATE TABLE `202_address` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '唯一id',
  `userId` int NOT NULL COMMENT '用户编号',
  `province` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '省',
  `city` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '市',
  `address` varchar(100) DEFAULT NULL COMMENT '详细地址',
  PRIMARY KEY (`id`)
)
<resultMap id="userMap" type="com.beiyou.model.User">
    <id column="id" property="id"/>
    <result column="tel" property="tel"/>
    <result column="password" property="password"/>
    <association property="userInfo" column="Id"  fetchType="lazy" javaType="com.beiyou.model.UserInfo"
     select="selectUserinfo">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
    </association>
    <collection property="addresses" column="id" fetchType="lazy" javaType="java.util.ArrayList"
           ofType="com.beiyou.model.Address"  select="selectAddress" >
        <id property="id" column="id"/>
        <result property="province" column="province"/>
        <result property="city" column="city"/>
        <result property="county" column="county"/>
        <result property="address" column="address"/>
    </collection>
</resultMap>
     <select id="selectAddr" resultType="com.beiyou.model.Address">
        select * from 202_address where userId = #{userId}
    </select>
使用Mapper注解,实现一对一和一对多关系查询
    @Results(id="userMap", value = {
            @Result(column = "id", property = "id", id = true),
            @Result(column = "tel", property = "tel"),
            @Result(column = "password", property = "password"),
            @Result(property = "userInfo", column = "id",
                    one = @One(select = "selectUserinfo",fetchType = FetchType.LAZY)), 可以不用写具体映射,但是用xml的时候,必须写
            @Result(column = "id",  property = "addresses" ,
                    many = @Many(select = "selectAddress",fetchType = FetchType.LAZY))
    })
}
    @Select("select  *  from 202_user  u   where u.id = #{id}")
    List<User> layeOne2One(int id);
     @Select("select  *  from 202_address where userId = #{id}")
    List<Address> selectAddress(Integer id);

测试代码
   @Test
    public void test(){
        UserMapper2 dao = sqlSession.getMapper(UserMapper2.class);
        List<User> users = dao.queryUserAll();
    }
Java
六 MyBatis缓存机制

mybatis.xml
   <settings>
        <setting name="cacheEnabled" value="true"/> //开启全局的二级缓存
    </settings>
//清空缓存数据
       @Options(flushCache = Options.FlushCachePolicy.TRUE)
 
       @Select(" select *  from  202_user where id= 46")
       User  one();
6.1 一级缓存
一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。
一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。
6.1.1何时清空一级缓存
如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。
6.1.2一级缓存无过期时间,只有生命周期
MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
6.2 二级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
同一个工厂生产的sqlsession,批次号相同.
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
6.2.1 二级缓存何时存入
在关闭sqlsession后(close或commit),才会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。
开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中
6.2.2二级缓存有过期时间
每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下.
6.2.3 执行流程
当 Mybatis 调用 Dao 层查询数据库时,先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。
       SqlSessionFactory级别缓存,会话工厂级别
        SqlSession s1 = sf.openSession();
        SqlSession s2 = sf.openSession();
        SqlSession s3 = sf.openSession();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    开发者必须自己配置二级缓存
    二级缓存是人工开启的,需要在XxxxMapper.xml 文件中加入如下开启
       方法一
            <cache eviction="FIFO" flushInterval="60000" size="5120" readOnly="true" />
            <select id="queryAll" resultType="book" useCache="false">  默认使用缓存,填写false此操作不让缓存
            select * from book
            </select>
       方法二
            @CacheNamespace(eviction = FifoCache.class, flushInterval = 60000, size = 1024, readWrite = true)
            public interface BookMapper {
                @Select("select * from book") 
                @Options(useCache = true)
                public List<Book> queryAll();
                @Select("select * from book where id = #{id}")
                public Book queryById(int id);
            }           
    注意:使用缓存时,最好给实体类序列化。
Java
Student.java
@Data
public class Student implements Serializable {
    private int id;
    private String name;
    private int age;
    private double money;
    private String info;
}
Plain Text
StudentMapper.java
@CacheNamespace 
public interface StudentMapper {
    @Select("select * from t_student")
    @Options(useCache = true) //开启或关闭二级缓存
    public List<Student> page();
    @Select("select * from t_student where id = #{id}")
   @Options(useCache = true)
    public Student queryById(int id);
}
Java
@Test
public void t5() {
    var session = sf.openSession();
    var sm = session.getMapper(StudentMapper.class);
    System.out.println(sm.page());
    System.out.println(sm.page());
    System.out.println(sm.page());
    System.out.println(sm.page());
    System.out.println("---------------------------");
    System.out.println(sm.queryById(5));
    session.commit();//将当前会话的查询,保存到二级缓存中,
    System.out.println(sm.queryById(5));
    System.out.println(sm.queryById(5));
    System.out.println(sm.queryById(5));
    System.out.println("----------------------");
    var s2 = sf.openSession();
    var sm2 = s2.getMapper(StudentMapper.class);
    System.out.println(sm2.queryById(5));
}
Plain Text
六 常见问题
1.MySQL连接数据库时,添加语句:“allowMultiQueries=true”的作用:
-  可以在sql语句后携带分号,实现多语句执行。 
-  可以执行批处理,同时发出多个SQL语句。 
2.找不到配置文件




















