Mybatis知识点全总结(二)

简介: Mybatis知识点全总结

5.执行SQL语句


前面我们对mybatis已经配置得差不多了,现在差不多就可以使用mybatis来执行SQL语句了。在项目的Test测试文件夹中创建一个MybatisTest的一个Java测试类,我们同时要到导入junit的maven依赖:


         <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>


然后创建一个名为testSelectAll的测试方法,编写测试select语句的相关代码,代码如下:


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 org.haiexijun.entity.Goods;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
public class MybatisTest {
    @Test
    public void testtestSelectAll() throws IOException {
        //利用Reader加载classpath下的mybatis-config.xml环形配置文件
        Reader reader=Resources.getResourceAsReader("mybatis-config.xml");
        //初始化SqlSessionFactory
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession sqlSession=null;
        try {
            sqlSession=sqlSessionFactory.openSession();
            sqlSession.selectList("goods.selectAll");
            List<Good> goods= sqlSession.selectList("goods.selectAll");
            for (Good g:goods){
                System.out.println(g.getSubTitle());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //如果type=”POOLED",代表连接池,close则是将连接回收到连接池中
            //如果type=“UNPOOLED",代表将连接关闭
            if (sqlSession!=null){
                sqlSession.close();
            }
        }
    }
}

运后能成功查询到相关的数据。


下面对代码进行一些步骤的解释:


(1)构建 SqlSessionFactory


每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory是Mybatis的核心对象。用于初始化Mybatis,创建SqlSession对象。要保证SqlSessionFactory在全局中唯一。


SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。


从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 可以使用任意的输入流(Reader、InputStream)实例。

如:


String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);


(2)从 SqlSessionFactory 中获取 SqlSession


既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。


SqlSession是Mybatis操作数据库的核心对象,底层使用JDBC与数据库进行交互。 SqlSession对象提供了数据表CRUD对应的方式。


看到这里,就算完成了对Mybatis的最基本的操作了。恭喜你!入门了。但是这只是入门而已,我们仍然能发现代码的很多问题。比如,我们的代码无法保证SqlSessionFactory在全局中唯一。所以我们可以对代码做进一步的优化改善。所以下面要封装一个MybatisUtils初始化工具类来对我们的代码进一步优化。


6.封装初始化工具类MybatisUtils


在项目的main下Java下创建一个org.haiexijun.utils的工具包,用于存放我们封装的工具类。

然后再utils包下面创建一个叫做MybatisUtils的java类。代码如下:


package org.haiexijun.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.Reader;
//工具类的方法和属性一般都用static关键字修饰
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory=null;
    //用于初始化静态对象
    static {
        Reader reader= null;
        try {
            reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
            //把异常抛出去,让使用到这个工具类的程序也知道我们这段代码出错了
            //ExceptionInInitializerError是初始化异常
            throw new ExceptionInInitializerError(e);
        }
    }
    //创建一个方法获取SqlSession对象
    public static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }
    //关闭(回收)SqlSession的连接的方法
    public static void closeSession(SqlSession session){
        if (session !=null){
            session.close();
        }
    }
}


我们编写好MybatisUtils初始化工具类后,我们再到之前编写的MybatisTest测试类中对它进行测试,代码如下:


    @Test
    public void testMybatisUtils(){
        SqlSession sqlSession=null;
        try {
            sqlSession=MybatisUtils.openSession();
            List<Good> goods= sqlSession.selectList("goods.selectAll");
            for (Good g:goods){
                System.out.println(g.getSubTitle());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            MybatisUtils.closeSession(sqlSession);
        }
    }


结果显示,使用MybatisUtils工具类后,既保证了SqlSessionFactory在全局中唯一,也使我们的代码更加的直观和整洁了。


你以为到这里就结束了吗?并没有哦。我们的代码还有一个很严重的bug没有解决。我们先来对比一下数据库中的t_goods数据表的字段名和我们实体类定义的字段名。如下图:


021ceffb620d49138dad94798267215e.png


Good的entity实体类的字段名:


26d0397bd85c44af835f212568f670be.png


我们会发现,有些字段的名字是相互对应的关系,如title和discount这两个字段。但如果字段的名字是由两个以上单词组成时,就不是相互对应的关系了。如good_id这个字段我们在实体里面写为goodId这种大驼峰的写法,但这种定义Mybatis无法对其识别。如果我们这时查询goodId,则查询结果全为null。解决办法就是对mybatis的核心配置文件中增加一个设置项<settings>,之后mybatis就会自动对数据表进行驼峰命名转换了。


在mybatis-config.xml中新增加如下代码:


    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>


7.设置(settings)


这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。我们就是在里面设置对数据表的驼峰命名转化的。

下面我们列出一些setting的选项:

image.png

image.png


一个配置完整的 settings 元素的示例如下:


<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>


8.SQL传参


前面案例里面,有用到select,查询t_goods表的所有的数据。但是,很多时候,我们要查询的数据可能并不是全部数据,而是我们指定要查询的数据。这时候,我们就不能再像以前那样把select语句给写死了。而是根据用户的输入,动态传入参数来查询。


传入一个参数时的写法


要实现SQL传参,就要用到select元素的一个属性:parameterType,来指定参数的类型。

比如我要通过Id值来查询数据:


    <select id="selectById" parameterType="Integer" resultType="org.haiexijun.entity.Goods">
           select * from t_goods where goods_id = #{value}
    </select>


parameterType指定传入的参数为Integer类型,然后在select语句里面用#{ }来表示传入的参数的占位。有点像JDBC里面preparedstatement的 ?。

在MybatisTest测试类中编写测试方法:


    @Test
    public void testSelectById(){
        SqlSession sqlSession=null;
        try {
            sqlSession=MybatisUtils.openSession();
            Good good= sqlSession.selectOne("goods.selectById",1603);
            System.out.println(good.getTitle());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            MybatisUtils.closeSession(sqlSession);
        }
    }


注意: 因为是通过id来查询数据,返回的数据只有一条。所以要用selectOne()方法 ,这个方法传入两个参数,第二个参数是sql语句要传入的参数值,要与之前select元素里面编写的parameterType要一致。比如传入1603,就指查找id为1603的good的信息。


要传入多个参数时的写法


如果要查询某一价格范围内的商品的信息,传入的参数为最大值和最小值这两个值,此时一个参数就不能完成查询了。但是Mybatis只支持设置一个parameterType。


遇到多参数的情况下,我们可以通过设置parameterType的值为java.util.Map,键值对的形式来传入多个参数。


新建一个select查询:


    <select id="selectByPriceRange" parameterType="java.util.Map" resultType="org.haiexijun.entity.Goods">
        select * from t_goods where current_price between #{min} and #{max} order by current_price limit 0,#{limit}
    </select>


可以看到,我们把parameterType的值定义为java.util.Map,select语句要传入三个参数:min,max和limit。

在MybatisTest测试类中编写测试方法


    @Test
    public void testSelectByPriceRange(){
        SqlSession sqlSession=null;
        try {
            sqlSession=MybatisUtils.openSession();
            Map param=new HashMap();
            param.put("min",100);
            param.put("max",500);
            param.put("limit",10);
            List<Good> goods= sqlSession.selectList("goods.selectByPriceRange",param);
            for (Good g:goods){
                System.out.println(g.getTitle()+":"+g.getCurrentPrice());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            MybatisUtils.closeSession(sqlSession);
        }
    }


我们创建了HashMap,并传入3个键值对,这三个键值对与select语句中要传入的参数一 一对应。键名也要对应。


9.多表关联查询


在实际开发中,大量的企业级应用,都是多表联合查询所产生的一个复杂的结果集。查询的结果字段会横跨很多张表。


获取多表关联查询结果


如果要进行多表关联查询,那么查询结果的类型就不是某个具体的实体(entity)了。此时我们要把resultType设置为java.util.Map。也就是说,多表关联查询后返回的结果为map类型。


编写一条select语句,查询t_goods表和t_category这两个表的中category_id相同的结果。


    <select id="selectGoodsMap" resultType="java.util.Map">
        select g.* , c.category_name from t_goods g,t_category c
        where g.category_id = c.category_id
    </select>


在MybatisTest测试类中编写测试方法:


    @Test
    public void testSelectGoodsMap(){
        SqlSession sqlSession=null;
        try {
            sqlSession=MybatisUtils.openSession();
            List<Map> list= sqlSession.selectList("goods.selectGoodsMap");
            for (Map m:list){
                System.out.println(m);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            MybatisUtils.closeSession(sqlSession);
        }
    }


运行这个测试方法后,如下:


a3b33acaeb2847f8b6b71f9e477896a7.png


我们看到所有的键名都是原数据表的字段名。而且会发现虽然Map可以存储查询结果,但是存储的结果并不是按顺序来排列的。这是因为HashMap是按哈希值来排序的。


把resultType设置为java.util.LinkedHashMap,因为LinkedHashMap是链表形式的HashMap,会按照顺序来存取。


    <select id="selectGoodsMap" resultType="java.util.LinkedHashMap">
        select g.* , c.category_name from t_goods g,t_category c
        where g.category_id = c.category_id
    </select>


更改后运行代码,就有序了:


daeb1bbc8116448daa0470a7058d0ced.png


利用LinkedHashMap,保存多表关联查询结果,Mybatis会将每条记录都包装为LinkedHashMap对象。Key是字段名,value是字段对应的属性值。字段类型会根据表结构自动判断。

优点:易于拓展和使用

缺点:太过于灵活了,无法进行编译时检查。


10.ResultMap结果映射


ResultMap可以将查询结果映射为复杂类型的java对象

ResultMap适用于Java对象保存多表关联的结果

ResultMap支持对象关联查询等高级特性。

resultMap 元素是 MyBatis 中最重要最强大的元素。

写一个案例来体验一下结果映射:

上面的案例我们用LinkedHashMap来保存了多表联合查询的结果。

如果此次我想要把多表联合查询的结果保存为Java对象的话,就要用到ResultMap结果映射。


先创建一个新的包:org.haiexijun.dto。

DTO(data transfer object)数据传输对象,是一个特殊的javaBean,对原始的对象进行扩展,用于数据保存和传递。

在org.haiexijun.dto包下面创建一个叫GoodsDTO的Java类。下面是代码:


package org.haiexijun.dto;
import org.haiexijun.entity.Goods;
public class GoodsDTO {
    //Goods实体的引用
    private Goods goods=new Goods();
    //其他字段
    private String categoryName;
    public Good getGoods() {
        return goods;
    }
    public void setGoods(Good goods) {
        this.goods = goods;
    }
    public String getCategoryName() {
        return categoryName;
    }
    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }
}


那么针对于这个DTO对象,如何让Mybatis自动对其进行对应的赋值呢?在这,我们就要使用ResultMap了。

配置ResultMap

(1)在goods.xml映射文件中创建新建一个resultMap节点,并对其进行配置:


    <!--结果映射-->
    <resultMap id="rmGoods" type="org.haiexijun.dto.GoodsDTO">
        <!--设置主键字段和属性映射-->
        <id property="goods.goodsID" column="good_id"/>
        <!--设置非主键字段与属性映射-->
        <result property="goods.title" column="title"/>
        <result property="goods.subTitle" column="sub_title"/>
        <result property="goods.originalCost" column="original_cost"/>
        <result property="goods.currentPrice" column="current_price"/>
        <result property="goods.discount" column="discount"/>
        <result property="goods.isFreeDelivery" column="is_free_delivery"/>
        <result property="goods.categoryId" column="category_id"/>
        <!--特殊的DTO的字段,property直接传入字段名-->
        <result property="categoryName" column="category_name"/>
    </resultMap>

resultMap的id属性是为了被下面的select元素引用而定义的。type表示把结果存为哪个DTO。resultMap还要顶义一些子标签,id标签是设置主键字段和属性映射,result标签是设置非主键字段与属性映射。每个子标签都有两个属性:property和column。column表示查询的字段名,property这指向了GoodsDTO里面的每个属性的名称,如果一个属性是额外的属性,则直接传入属性名。


(2)在goods.xml映射文件中新建一个select元素,并为其指定resultMap属性,属性值为上面定义的resultMap的id属性值rmGoods:


    <select id="selectGoodsDTO" resultMap="rmGoods">
        select g.* , c.category_name from t_goods g,t_category c
        where g.category_id = c.category_id
    </select>


  1. 在MybatisTest测试类中编写测试方法:


    @Test
    public void testSelectGoodsDTO(){
        SqlSession sqlSession=null;
        try {
            sqlSession=MybatisUtils.openSession();
            List<GoodsDTO> list= sqlSession.selectList("goods.selectGoodsDTO");
            for (GoodsDTO g:list){
                System.out.println(g.getGoods().getTitle());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            MybatisUtils.closeSession(sqlSession);
        }
    }
相关文章
|
SQL XML 存储
Mybatis知识点总结
Mybatis知识点总结
211 0
|
SQL Java 数据库连接
MyBatis 小知识点补充(#{} 和 ${})
MyBatis 小知识点补充(#{} 和 ${})
73 0
|
SQL XML Java
MyBatis知识点笔记
MyBatis知识点笔记
191 0
MyBatis知识点笔记
|
SQL 缓存 Java
Mybatis核心知识点整理,常见面试题总结必备!
Mybatis核心知识点整理,面试必备! 🍅 Java学习路线:搬砖工的Java学习路线 🍅 作者:程序员小王 🍅 程序员小王的博客:https://www.wolai.com/wnaghengjie/ahNwvAUPG2Hb1Sy7Z8waaF 🍅 扫描主页左侧二维码,加我微信 一起学习、一起进步 🍅 欢迎点赞 👍 收藏 ⭐留言 📝
228 0
Mybatis核心知识点整理,常见面试题总结必备!
|
SQL 存储 缓存
Mybatis总结
1、MyBatis简介 1.1 MyBatis历史 1.2 MyBatis特性 2、搭建MyBatis 2.1 MySQL不同版本的注意事项 2.2 创建MyBatis的映射文件 3、核心配置文件详解 4、MyBatis获取参数值的两种方式 4.1 单个字面量类型的参数 4.2 多个字面量类型的参数 4.3 map集合类型的参数 4.4 实体类类型的参数 4.5 使用@Param标识参数 5、MyBatis特殊的SQL执行
172 0
Mybatis总结
|
XML SQL Java
Mybatis知识点全总结(四)
Mybatis知识点全总结
161 0
Mybatis知识点全总结(四)
|
SQL 存储 缓存
Mybatis知识点全总结(三)
Mybatis知识点全总结
216 0
Mybatis知识点全总结(三)
|
缓存 Java 数据库连接
mybatis的缓存总结
mybatis的缓存总结
124 0
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
110 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。