红泥小火炉


快速学Mybatis(一)

Nathaniel 2021-09-03 288浏览 0条评论
首页/正文
分享到: / / / /

快速介绍

Mybatis是一个半自动的ORM(object-relation mapping,对象关系映射)框架,之所以是半自动,是因为其将sql编写的灵活性增加了,允许sql定制化开发,避免了JDBC中的大部分代码编写,通过xml或者注解的方式可以方便地将java对象同数据库记录进行映射,只需要在程序中操作java对象,就可以操作数据库相关记录,为持久化提供了良好的解决方案。

快速使用

  • 引入Mybatis依赖
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!-- mysql依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.35</version>
</dependency>
  • 编写全局配置文件(globalConfig.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="StudentMapper.xml" />
    </mappers>
</configuration>
  • 编写数据库配置文件(db.properties)
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis
db.username=root
db.password=123456
  • 编写映射文件(StudentMapper.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="develop">
    
    <select id="findById" 
            parameterType="int" 
            resultType="site.zhaoyangjue.entity.Student">
        select * from student where id = #{id}
    </select>

    <select id="findByName" 
            parameterType="java.lang.String"
            resultType="site.zhaoyangjue.entity.Student">
        select * from student where name like '%${value}%'
    </select>
</mapper>
<!--
如果基于Mapper形式的方式开发,则此文件中的namespace与Mapper接口的类路径要相同
	id要与接口名称相同,
	parameterType要与接口方法入参类型相同
	resultType类型要与接口方法返回值类型相同
-->
  • 编写java对象实体类(Student)
public class Student {
	private int id;
	private String name;
	private Date birthday;
	private String sex;
	private String address;
    // 此处省略getter/setter、toString方法
}
  • 编写dao相关类(StudentDao,StudentDaoImpl)
public interface StudentDao {
    Student findById(int id) throws Exception;
    List<Student> findByName(String name) throws Exception;
    void insertStudent(Student student) throws Exception;
}
public class StudentDaoImpl implements StudentDao {

    private SqlSessionFactory sqlSessionFactory;

    public StudentDaoImpl(SqlSessionFactory sqlSessionFactory){
        this.sqlSessionFactory = sqlSessionFactory;
    }
    @Override
    public Student findById(int id) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Student student = null;
        try{
            student = sqlSession.selectOne("develop.findById", id);
        } finally {
            sqlSession.close();
        }
        return student;
    }

    @Override
    public List<Student> findByName(String name) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<Student> studentList = null;
        try{
            studentList = sqlSession.selectList("develop.findByName", name);
        } finally {
            sqlSession.close();
        }
        return studentList;
    }

    @Override
    public void insertStudent(Student student) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            sqlSession.insert("develop.insertStudent", student);
            sqlSession.commit();
        } finally {
            sqlSession.close();
        }
    }
}
// 基于xml方式的开发方式,则只需要根据session获取Mapper操作对象即可:
// StudentMapper studentMapper = session.getMapper(StudentMapper.class);
// 此处的studentMapper是一个接口。
  • 组织查询操作
public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() throws Exception{
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        InputStream resourceAsStream = Resources.getResourceAsStream("globalConfig.xml");
        sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
    }

    @Test
    public void testFindById() throws Exception {
        StudentDaoImpl studentDao = new StudentDaoImpl(sqlSessionFactory);
        Student studentById = studentDao.findById(2);
        System.out.println(studentById);
    }

    @Test
    public void testFindByName() throws Exception {
        StudentDaoImpl studentDao = new StudentDaoImpl(sqlSessionFactory);
        List<Student> studentList = studentDao.findByName("张三");
        System.out.println(studentList);
    }
    @Test
    public void testInsertStudent() throws Exception {
        StudentDaoImpl studentDao = new StudentDaoImpl(sqlSessionFactory);
        Student student = new Student();
        student.setId(5);
        student.setName("小七");
        student.setAddress("上海市");
        student.setBirthday(new Date());
        student.setSex("男");
        studentDao.insertStudent(student);
    }
}

快速总结

#{}和${}区别

在组织sql时都用到了#{}和${},他们的区别在于:

#{} ${}
等同于jdbc中的PreparedStatement(?) 等同于jdbc中的Statement(+)
输入映射时,会对参数进行类型解析 输入映射时,将参数原样输出
对简单类型的输入映射时,参数名称可以任意 对简单类型的输入映射时,参数名称必须是value
不存在sql注入风险 存在sql注入风险
是通过反射获取数据(StaticSqlSource) 以OGNL表达式的形式随着嵌套关系而发生层级变化(DynamicSqlSource)

开发方式

基于xml方式开发和基于注解进行开发(@Select(sql语句),@Insert(sql语句)的形式,标注在接口方法声明上,一般比较少会用到)

在基于xml方式开发时,需要注意以下几点:

  • Mapper接口的类路径要与Mapper.xml文件中的namespace相同;
  • Mapper接口方法名称要与Mapper.xml中定义的每一个statement的id相同;
  • Mapper接口方法入参类型要与Mapper.xml中定义的每一个sql的parameterType类型相同;
  • Mapper接口方法返回值类型要与Mapper.xml定义的每个sql的resultType类型相同。

全局配置文件

配置项 含义说明 备注
properties 定义属性 通过resource属性引入properties文件,定义property字标签定义属性和属性值,加载属性的顺序是加载元素体内的属性,然后加载元素中的resource或url属性,如果出现同名属性,会发生覆盖
settings 定义全局配置参数
typeAliases 定义别名 简化类型名称编写,默认是支持的,alias指定别名,type指定类型路径
typeHandlers 类型处理器 将java类型转换为JDBC类型并转换为数据库类型
objectFactory 对象工厂
plugins 定义插件
environments 环境相关的属性集合
environment 环境子属性对象
transactionManager 事务管理
dataSource 定义数据源
mappers 映射器 resource指定相对路径,url指定绝对路径,class指定mapper接口类路径

上述程序在StudentMapper.xml中出现的parameterType用于定义输入参数的java类型;resultType用于定义查询结果的映射类型。

输入输出映射

parameterType用于定义输入参数的java类型,可以定义的java类型有简单类型、普通对象类型、Map和List类型

resultType用于定义输出参数的java类型,可以定义的java类型有简单类型、普通对象类型、Map类型。在使用时,查询的列名需要同普通对象类型中的属性名称保持一致。

resultMap用于将列名和属性名作为一个对应关系,此时查询列名和普通对象类型中的参数可以不一致。

<!-- type属性:指定映射的对象类型 id:resultMap的唯一标识 --><resultMap type="student" id="userListResultMap">    <!-- id标签:映射查询结果的唯一列(主键列)column:查询sql列名 property:映射对象的属性名 -->    <id column="id" property="id"/>    <!-- result标签:映射除id之外的其他字段 -->    <result column="name" property="stuName"/>    <result column="birthday" property="birthday"/></resultMap>

快速升级

关联查询

关联查询分为一对一、一对多、多对多,其中多对多可以看做是双向的一对多或者多对一,所以重点关注一对一和一对多即可。

  • 一对一

在一对一的场景下,可以使用resultMap,也可以使用resultType。

新增一对一的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"><mapper namespace="site.zhaoyangjue.dao.SidDao">    <resultMap id="stuInfoWithStudentResultMap" type="site.zhaoyangjue.entity.StuInfo">        <id column="id" property="id"></id>        <result column="info" property="info"></result>        <result column="s_id" property="sId"></result>        <!--association定义一对一映射,property定义映射属性对象,javaType定义映射属性对象类型-->        <association property="student" javaType="site.zhaoyangjue.entity.Student">            <id column="id" property="id"></id>            <result column="name" property="name"></result>            <result column="address" property="address"></result>        </association>    </resultMap>    <select id="findStuInfoWithStudent" resultMap="stuInfoWithStudentResultMap">         select a.id,a.info,a.s_id,b.id,b.id,b.address from stu_info a join student b on a.s_id=b.id    </select></mapper>
  • 一对多

在一对多场景下,只能使用resultMap,因为查询结果是多条记录。

<?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="site.zhaoyangjue.dao.ClassInfoDao">    <resultMap id="queryStudentsWithClassMap" type="site.zhaoyangjue.entity.ClassInfo">        <id column="id" property="id"></id>        <result column="name" property="cname"></result>    	<!--使用collection定义一对多映射,property定义多的那一方的信息,ofType用于指定多的那一方的对象类型-->        <collection property="studentList" ofType="site.zhaoyangjue.entity.Student">            <id column="id" property="id"></id>            <result column="name" property="name"></result>            <result column="address" property="address"></result>        </collection>    </resultMap>    <select id="queryStudentsWithClass" resultMap="queryStudentsWithClassMap">        select a.id,a.name,a.address,b.name,b.id from student a join class_info b on a.c_id=b.id    </select></mapper>

延迟加载

延迟加载属于关联查询场景下的优化操作,在进行查询时,按照设置的延迟规则对关联对象(对主对象没有作用)进行延迟查询,一定程度上可以减少数据库压力。根据执行时机,可以分为以下几种:

  • 直接加载:主加载对象执行完select之后,立刻执行关联对象的select.
<settings>	<setting name="lazyLoadingEnabled" value="false"/></settings>
  • 侵入式延迟:只有当要访问主加载对象的某属性时,才会执行关联对象的select
<settings>	<setting name="lazyLoadingEnabled" value="true"/>    <setting name="aggressiveLazyLoading" value="true"/></settings>
  • 深度延迟:只有真正访问关联对象的详情时,才会执行关联对象的select。该模式在关联对象对应表比较多的时候会产生N+1问题,即查询关联对象对应的数据表多次。
<settings>	<setting name="lazyLoadingEnabled" value="true"/>    <setting name="aggressiveLazyLoading" value="false"/></settings>

动态SQL

if标签

<if test="condition">	branch</if>

where标签

where标签会处理后面的第一个and,从而避免1=1查询带来的性能问题

<where>where-info</where>

sql标签

可以将sql片段进行封装,无需多次编写,只需要在使用时引用即可

<sql id="sql片段名称">    sql片段</sql>

include标签

可以将sql片段引用到其他的sql中

<include refid="namespace.sql片段名称"></include>

foreach标签

<foreach collection="集合的名称" item="集合中的元素变量" open="拼接在遍历sql的前面" close="拼接在遍历sql后面" separator="遍历sql之间的分隔符号">			#{参与遍历的字段}</foreach>

缓存

  • 一级缓存

一级缓存是SqlSession级别的缓存,其内部采用hashMap来存储缓存数据,不用的sqlSession之间的缓存相互独立。mybatis默认是开启的。一级缓存中数据的生命周期是同sqlSession一致的,随着SqlSession的消亡而消亡。

  • 二级缓存

二级缓存是mapper(namespace)级别的,二级缓存可以简单理解为基于mapper级别的一级缓存,只不过mapper已经是mybatis中最大的缓存了。mybatis默认是没有开启二级缓存的。

<settings>	<setting name="cacheEnabled" value="true"></setting></settings><!--单个mapper映射文件,默认使用的是perpetualCache--><cache></cache>

某一个statement想要禁用二级缓存,可以在对应的查询标签中设置useCache=false

二级缓存的刷新(flushCache),默认对于select语句为false,insert、update、delete语句为true。在设置二级缓存的刷新时要将二级缓存打开,同时要设置刷新间隔属性(flushInterval,单位为毫秒)。

最后修改:2021-09-03 14:33:50 © 著作权归作者所有
上一篇

评论列表

还没有人评论哦~赶快抢占沙发吧~

博客信息

  • 文章数目 12
  • 标签数目 7
  • 运行天数
  • 最后活动

广告

文章目录