Mybatis【笔记】

MyBatis

框架:SSM:spring springMVC mybatis(特点:配置文件很多)

​ spring boot

​ spring cloud

1、什么是mybatis

​ 持久层框架,与数据库相关的操作(dao层)可以使用mybatis来实现

2、为什么要用mybatis

​ 频繁连接释放资源

​ 封装查询结果

​ 之前的方式存在sql硬编码问题(如果sql发送改变,需要去修改java代码,项目需要重新编译/打包/…)

​ 解决sql硬编码问题:把sql语句和java代码分离

​ 把sql语句单独的写在一个文件中(.xml),把写sql的xml文件叫做sql映射文件

3、执行流程

​ 了解mybatis的执行流程,能够更好的去理解代码

​ 1、加载mybatis核心配置文件(比如:数据连接信息/数据源信息/…)

​ sql映射文件:写sql语句

​ 2、读取sql映射文件

​ sqlSessionFactroy工厂,生成SqlSession对象

​ SqlSession对象,可以去执行sql

​ 提供很多增删改查方法

​ **注意:**工厂在生成SqlSession需要用配置信息

​ 3、mybatis中的核心API

​ SqlSessionFactory:生成SqlSession对象

​ SqlSession:执行sql

4、mybaits快速入门

1、添加依赖

在recources文件下创建sqlMapConfig.xml

​ mybaits核心配置从官网拷贝过来就可以了

<?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="${url}"/>
        <property name="username" value="root"/>
        <property name="password" value="root}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
      <!-- 添加sql -->
    <mapper resource="com.ycy.mapper.UserMapper"/>
  </mappers>
</configuration>

2、sql映射文件

​ student:

​ 学生相关sql—>Student.xml

​ user:

​ 用户相关sql—>user.xml

​ class:

​ 班级相关sql—>class.xml

创建操作user的sql映射文件:

​ UserMapper.xml

创建mybatis核心配置文件:

​ 在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">
<mapper namespace="UserMapper">
  <select id="findAll" resultType="com.ycy.entity.User">
    select * from tb_user
  </select>
</mapper>

测试

 public void test(){
//        //加载核心配置文件
//        InputStream reader= MyTest.class.getClassLoader().getResourceAsStream("sqlMapConfig.xml");
//        //获取sqlSessionFactory
//        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);//构建工厂需要配置信息
//        //根据工厂获取SqlSession对象
//        SqlSession sqlSession = factory.openSession();
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //使用sqlSession对象执行sql
        List<User> list = sqlSession.selectList("UserMapper.findAll");参数:代表要执行的sql的唯一标识--->namespace.id

        //打印
        for (User user : list) {
            System.out.println(user);
        }
    }

3、使用方式(步骤):

​ 1、写核心配置文件

​ 2、写sql映射文件

​ 3、创建factory工厂对象

​ 4、创建sqlSession对象

​ 5、通过sqlSession对象执行sql

4、优化:

1、工具类:获取sqlSession对象
public class MyBatisUtils {
    //工厂只能有一个
    private static SqlSessionFactory factory;
    static{
        //加载核心配置文件
        InputStream reader= MyBatisUtils.class.getClassLoader().getResourceAsStream("sqlMapConfig.xml");
        //获取sqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);//构建工厂需要配置信息

    }
    //提供一个伊公共的静态的方法,返回sqlSession对象
    public static SqlSession getSqlSession(){
        //根据工厂获取SqlSession对象
        SqlSession sqlSession = factory.openSession();
        return sqlSession;
    }
}
2、执行sql的方式
sqlSession.selectList("UserMapper.findAll");//namespance.id

回到以前熟悉的操作方式

​ UserDao:

​ findAll()

​ userDao.findAll()

(1)创建接口

​ 在mybatis中,dao层的包名一般叫mapper

​ UserDao—>UserMapper

​ 创建UserMapper接口

(2)创建接口对应的sql映射文件

​ 在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">
<mapper namespace="com.ycy.mapper.UserMapper">
  <select id="findAll" resultType="com.ycy.entity.User">
    select * from tb_user
  </select>
</mapper>

namespace的值是对应接口的权限命名:包名.类名

id值对应接口中的方法名

测试:

public void test(){
//根据工厂获取SqlSession对象
//        SqlSession sqlSession = factory.openSession();
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
	//获取接口,调用接口中方法
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.findAll();
        //打印
        for (User user : list) {
            System.out.println(user);
        }
    }
3、优化后的使用步骤

(1)创建工具类

(2)通过工具类拿到SqlSession

(3)使用SqlSession拿到Mapper接口

(4)调用Mapper接口中的方法

5、MyBatis核心配置文件

  • configuration(配置)---->根标签
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

**注意:**子标签必须按照固定的顺序来写

1、properties(属性)

​ 指定外部文件路径

2、settings(设置)

​ 缓存设置

​ 延迟加载设置

​ …

3、typeAliases(类型别名)

​ 设置别名

方式一:一个一个的设置(麻烦)
<!--    设置别名-->
    <typeAliases>
        <typeAlias type="com.ht.entity.User" alias="user"/>
    </typeAliases>
方式二:自动扫描(自动取别名):名字只要实体类的名字字母一样即可(不区分大小写)
<typeAliases>
        <package name="com.ht.entity"/>
</typeAliases>

4、environments(环境配置)

可以配置多个环境

<environments default="mysql1">//选择使用的环境(选择环境id)
     <environment id="mysql1">//配置某个具体的环境,给一个id
          <transactionManager type="JDBC"/>//设置事务管理器
          <dataSource type="POOLED">//设置数据源:
              //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>

如何更新数据源:

1、添加依赖

<!--druid数据源  pom.xml(添加依赖)-->
<dependency>
	<groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>

2、构建数据源适配器

//创建数据源适配器
pulibc class DruidDataSourceFactory extends UnpooledDataSourceFactory{
    //构造方法
    public DruidDataSourceFactory(){
        //指定数据源
        this.dataSource = new DruidDataSource();
        
    }
}

3、修改数据源配置

 <environments default="mysql1">
        <!-- 第一个环境  -->
        <environment id="mysql1">
            <transactionManager type="JDBC"/>使用自定义数据源适配器
            <dataSource type="com.ht.factory.DruidDataSourceFactory">
                <property name="driverClassName" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

5、mappers(指定映射文件路径)

<!--添加sql映射文件路径--> 
<mappers>
        <!--  <mapper resource="com/ycy/mapper/UserMapper.xml"/>-->//方式一:指定xml文件的路径
        <!--<mapper class="com.ht.mapper.UserMapper"/>-->//方式二:直接Mapper接口路径
    
    	<package name="com.ht.mapper"/>//方式三:自动扫描(指定扫描路径)
    </mappers>

6、sql映射文件

​ 写sql:增删改查

XML映射器

​ select

​ insert,update和delete

<select>查询</select>
<insert>增加</insert>
<update>修改</update>
<delete>删除</delete>
1、添加数据
<!--添加-->
<insert id = "addUser" parameterType="User">
	insert into tb_user(username,password) values(#{username},#{password})
</insert>

**注意:**现在执行的是更新操作,但是没有提交事务

//提交(手动提交)
sqlSession.commit();//提交事务
sqlSession.close();//关闭
//自动提交
//在获取sqlSession对象时
//如果openSession()中没有传递参数,关闭自动提交
     public static SqlSession getSqlSession(){
        //根据工厂获取SqlSession对象
         //没有自动提交
        //SqlSession sqlSession = factory.openSession();
         //开启自动提交
        SqlSession sqlSession = factory.openSession(true);
        return sqlSession;
    }

如果想要获取添加数据后的id:

​ 把id值赋值给参数的属性

<!--添加-->
<insert id = "addUser" parameterType="User" useGeneratedKeys = "true" keyColumn="id" keyProperty="id">
	insert into tb_user(username,password) values(#{username},#{password})
</insert>
2、修改数据
<update id = "updateUser" parameterType = "User">
	update tb_user set username = #{username},password=#{password} where id = #{id}
</update>
3、删除数据
<delete id = "deleteById" parameterType="int">
	delete * from tb_user where id = #{id}
</delete>

parameterType:

​ 简单数据类型的别名

​ 参数类型 别名

​ int _int

​ short _short

​ …

​ String string

​ Integer int

​ Short short

4、查询数据
<select id = "findById" parameterType = "int" resultType="User">
    select * from tb_user where id = #{id}
</select>

resultType:

​ 指定返回值类型

​ 把查询结果自动封装到指定的返回值类型中

​ 自动封装:字段名和属性名保持一致

​ 字段名—>属性名

​ 字段名不一致如何解决?resultMap

7、自定义映射标签

问题:字段名和属性名不一致,无法完成自动映射(封装)

解决:自定义映射关系

​ resultMap:自定义映射关系

<resultMap id = "stdMap" type = "Student">
	<!--主键-->
    <id column="id" property="xh">
    <!--非主键-->
    <result column = "username" property = "stdName"/>
    <result property =  "gender" column = "sex"/>
    <result property = "stdAge" column = "age"/>
</resultMap>
<select id = "findById" parameterType = "int" resultMap="stdMap">
    select * from tb_user where id = #{id}
</select>

resultType:使用默认的映射关系(字段名和属性名一致),默认的映射关系不能满足,所以要使用自定义的映射关系(不能用resultType属性)

使用resultMap标签去自定义映射关系

使用resultMap属性去指定要使用的自定义映射关系

8、参数传递

1、一个参数

​ 一个简单类型的参数

在sql中占位符中间的名字随便写,一般和参数名保持一致

2、多个参数

​ 解决方案一:使用@Param注解

​ 占位符中的名字要和注解里面的名字一致

User find (@Param("username") String username,@Param("password") String password)

​ 解决方案二:把参数封装到map集合中

​ 占位符中的名字要和map集合中的key值一致

User findByParamMap(Map<String,Object> map);

​ 解决方案三:参数封装到实体类中

​ 占位符中名字和实体类中属性保持一致

9、动态sql

​ 根据传递的不同参数,动态的拼接成不同的sql

//StudentMapper.java
//条件查询:所有查询条件封装到Student对象中
List<Student> findByCondition(Student student);

需要根据参数的情况进行判断:

​ 如果username,sql中就把username拼接上

​ 如果sex,在sql中就把sex拼接上

​ …

1、< if >判断

<if test="判断条件">满足条件执行的内容</if>


<if test="username!=null">
	username = #{...}
</if>
<if test="sex!=null">
	sex = #{...}
</if>
<!--StudentMapper.xml-->
<mapper>
	<select id = "findByCondition" parameterType="Student" resultMap="stdMap">
        select* from tb_student where 1=1
        <if test= "xh!=null">
        	and id = #{xh}
        </if>
        <if test="stdName!=null">
           and username=#{stdName}
        </if>
        <if test="gender!=null">
        	and sex=#{gender}
        </if>
        <if test="classno!=null">
        	and classno=#{classno}
        </if>
        
    </select>
</mapper>

Mybatis查询sql(日志)

在resources下添加log4j.properties文件

#全局日志配置
log4j.rootLogger=ERROR,stdout
#MyBatis 日志配置
log4j.logger.com.ycy.mapper=DEBUG
#控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=ort.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] -%m%n

2、< where >

在sql后面拼接where关键字,会自动的把多余的and去掉
<!--StudentMapper.xml-->
<mapper>
	<select id = "findByCondition" parameterType="Student" resultMap="stdMap">
        select* from tb_student
        <where>
        	<if test= "xh!=null">
        		id = #{xh}
        	</if>
        	<if test="stdName!=null">
           		username=#{stdName}
        	</if>
        	<if test="gender!=null">
        		sex=#{gender}
        	</if>
        	<if test="classno!=null">
        		classno=#{classno}
        	</if>
        </where>
    </select>
</mapper>

3、< set >

相当于在sql后面拼接set关键字,并且会自动的把多余的and去掉
<update id = "updateStudent" parameterType = "Student">
	update tb_student
    <set>
    	<if test = "stdName!=null">
    		username = #{stdName}
    	</if>
    	<if test = "gender!=null">
    		sex = #{gender}
    	</if>
    	<if test = "stdAge!=null">
    		age=#{stdAge}
   		</if>
    	<if test="classno!=null">
    		classno=#{classno}
    	</if>
    </set>
</update>

4、< foreach >

​ 用来循环的

<!--批量删除-->
<delete id = "batchDel">
    delete * from tb_student where id in
    <foreach collection = "array" item = "id" open="(" separator="," close=")">
        #{id}
    </foreach>

</delete>

属性:

​ open:数组的开始标志

​ separator:数组之间元素的分隔

​ close:数组的结束标志

​ collection:循环的数据类型:array(数组) /list(list集合)

​ foreach:把循环出来的数据保存到一个变量中

​ #{id}:使用循环出来的数据(变量id)

10、关联查询

1、一对一

​ Person --添加–> IdCard

public class Person{
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private IdCard idCard;
}
//方式一:根据id查询用户信息(查询出身份证信息)
Person findById(Integer id);

//方式二:根据id查询用户的信息
Person findById2(Integer id);
<mapper namespace="com.ycy.mapper.PersonMapper">
    <resultMap id = "personAndCard" type="Person">
    <id property = "id" column="id"/>
        <result property = "name" column = "name"/>
        <result property = "sex" column = "sex"/>
        <result property = "age" column = "age"/>
        <association property = "idCard" javaType = "IdCard" selet = "com.ycy.mapper.IdCardMapper.findByPersonId" column="id"/>
    </resultMap>
<select id = "findById" parameterType="int" resultType="Person">
    select * from tb_person
    <where>
    	id=#{id}
    </where>
    </select>
</mapper>
<select id="findByPersonId" parameterType="int" resultType="Idcard">
    select b.*
    from tb_person a,tb_idcard b
    where a.card_id = b.id and a.id=#{id}
</select>

实现1对1的关联查询:

​ 需要使用< association >

**注意:**在< resultMap>中使用

​ 实现:

​ 方式一(分步查询):去写一个person的id查询idCard的信息的sql,然后去调用即可(执行2个sql)

​ 方式二(嵌套结果):一次性把数据查询出来

<resultMap id="personAndCard2" type="Person">
	<id property="id" column="id"/>
    <result property="name" column ="name"/>
    <result property = "sex" column = "sex"/>
    <result property = "age" column = "age"/>
    <association property = "idCard" javaType ="IdCard">
    <id property="id" column="card_id"/>
        <result property = "cardnum" column="cardnum"/>
        
    </association>
</resultMap>
<select id = "findById2" parameterType = "int" resultMap="personAndCard2">
	select a.*,b.cardnum
    from tb_person a,tb_idcard b
    where a.card_id = b.id and a.id=#{id}
</select>

2、一对多

方式一:(不好用不记了)

方式二:一次性查询出所有需要的数据

public class Student2{
    private Integer id;
    private String username;
    private String sex;
    private Integer age;
}
public class Classinfo{
    private Integer id;
    private String classname;
    List<Student> students;
}
public interface ClassinfoMapper{
    //根据id查询班级信息(把班级学生信息查询出来)
    Classinfo findById(Integer id);
}
<mapper namespace="com.ycy.mapper.ClassinfoMapper">
	<!--根据id查询班级信息-->
    <resultMap id="classAndStd" type="Classinfo"/>
    <id property = "id" column ="classno"/>
    <result property = "classname" column="classname"/>
    <collection property = "student" ofType="Student">
        <id property = "id" column = "id"/>
        <result property ="username" column = "username"/>
        <result property = "sex" column = "sex"/>
        <result property = "age" column = "age"/>
    </collection>
    <select id = "findById" parameterType = "int" resultType="Classinfo">
    select a.classname,b.* 
    from tb_class a,tb_student b
    where a.id=b.classno and a.id=#{id}
        
    </select>
</mapper>

标签:

​ < collection> -->自定义一对多映射关系

**注意:**在< resultMap>中使用

3、多对多

11、MaBatis缓存和延迟加载

1、MaBatis缓存

1.一级缓存
  • 第一次查询 ,首先会从一级缓存中查找有没有想要查询的数据,如果没有,这时从数据库查询,查询之后把查询结果保存到一级缓存中
  • 第二次查询,仍然会从一级缓存中查找,发现一级缓存中有,直接从一级缓存中拿

总结:1、查询数据先从缓存中进行查询,如果缓存中有,直接使用,不云查询数据库

​ 2、如果缓存中没有,在从数据库查询数据,并查询到的数据保存到缓存中

​ 3、如果执行的更新操作(insert /update/delete),会清空缓存

​ 4、一级缓存是基于SqlSession级别

​ 5、在同一个SqlSession下的不同的Mapper共用同一级缓存

​ 6、不同的SqlSession下,会再次执行sql语句

2.二级缓存(namespace)

**注意:**二级缓存默认关闭,需要开启,(一般没有特殊需求,不开启二级缓存)

**注意:**二级缓存的数据需要进行序列化

第一步:核心配置文件中

<!--sqlMapConfig下-->
<setting>
	<!--开启二级缓存(全局)-->
    <setting name = "cacheEnabled" value= "true"/>
</setting>

第二步:sql映射文件

<!--开始局部二级缓存-->
<cache></cache>

第三步:序列化实体类

注意:一级缓存缓存的是对象(缓存的哪个对象,拿出来的就是哪个对象)

​ 二级缓存缓存的是数据(数据进行序列化,保存的拿出来的不是同一个对象)

2、延迟加载(采用分步查询)

如果需要查询出关联的数据,叫做立即加载

如果不需要查询出关联的数据,叫做延迟加载(懒加载)

一般而言:

  • 一对一:采用立即加载
  • 一对多:采用延迟加载

延迟加载特点:

​ 当你采用延迟加载策略,如果没有使用到关联数据,不会进行关联查询,如果使用到了关联数据,会查询关联数据(按需要加载)

开启延迟加载:

核心配置文件

<setting>
	<!--开记延迟加载(全局)-->
    <setting name="lazyLoadingEnabled" value="true"/>
</setting>

设置局部的加载策略

<result property = "age" column = "age"/>
<association property = "idCard" javaType = "IdCard" select ="com.ycy.mapper.IdCardMapper.findByPersonId" column = "id" fetchType="eager"

总结:延迟加载的效果

​ 开启全局的延迟加载

​ 设置局部的延迟加载策略

3、分页插件

方便的查询分页数据

使用:

​ 1、添加分页插件(依赖:pom.xml)

<!--分页插件-->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

​ 2、设置分页插件(核心配置文件中)

<!--引入pageHelper插件-->
<!--注意这里要写成PageInterceptor,5.0之前的版本都是写PageHelper,5.0之后要换成PageInterceptor-->
<plugins>
	<plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--reasonable:分页合理化参数,默认值为false,当该参数设置为true时,pageNum<=0时会查询第一页,pageNum>pages(超过总数时),会查询最后一页。-->
        <property name ="helperDialect" value="mysql"/>
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>

​ 3、进行分页查询

提供一个查询所有数据的方法即可

Lisr<Student> findAll();
<!--查询所有-->
<select id = "findAll" resultType="stdMap">
	select * from tb_student
</select>

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    //这是分页信息
    //startPage(int pageNum,int pageSize)
    //pageNum:当前页
    //pageSize:每页显示多少条数据
    PageHelper.startPage(2,3);
    //调用查询所有数据的方法s
    List<Student> all = mapper.findAll();
    //把分页数据封装到一个对象中
    PageInfo<Student> pageInfo = new PageInfo(all);
    System.out.println(pageInfo)
    sqlSession.close();
}
  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值