• MyBatis


    本人学疏才浅,如有错误请各位大牛批评指正!转载请注明出处!https://www.cnblogs.com/lee-yangyaozi/p/11040801.html

    什么是MyBatis
    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO 为数据库中的记录。

     

    MyBatis的特点

    简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

    灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

    解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。提供映射标签,支持对象与数据库的ORM字段关系映射,提供对象关系映射标签,支持对象关系组建维护,提供XML标签,支持编写动态sql。

     

    MyBatis的优缺点

    1.sql语句与代码分离,存放于xml配置文件中:

    优点:便于维护管理,不用在java代码中找这些语句;
    缺点:JDBC方式可以用用打断点的方式调试,但是Mybatis不能,需要通过log4j日志输出日志信息帮助调试,然后在配置文件中修改。

    2.用逻辑标签控制动态SQL的拼接:

    优点:用标签代替编写逻辑代码;
    缺点:拼接复杂SQL语句时,没有代码灵活,拼写比较复杂。不要使用变通的手段来应对这种复杂的语句。

    3.查询的结果集与java对象自动映射:

    优点:保证名称相同,配置好映射关系即可自动映射或者不配置映射关系,通过配置列名=字段名也可完成自动映射。
    缺点:对开发人员所写的SQL依赖很强。

    4.编写原生SQL:

    优点:接近JDBC,比较灵活。
    缺点:对SQL语句依赖程度很高;并且属于半自动,数据库移植比较麻烦,比如mysql数据库编程Oracle数据库,部分的sql语句需要调整。

     

    使用MyBatis

    1.导入jar包

    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
    </dependency>
    
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.0</version>
    </dependency>
    
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    </dependency>
    
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
    </dependency>

    其中log4j需要一个properties配置文件

    # Global logging configuration
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

     

    2.编写主配置文件(一般命名SqlMapConfig.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>
    
    <!-- 配置myabtis的默认数据源-->
    <environments default="devlopment">
    <!-- 数据源-->
    <environment id="devlopment">
    <!--事务管理 -->
    <transactionManager type="JDBC"></transactionManager>
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/databaseName?characterEncoding=utf-8"></property>
    <property name="username" value="username"></property>
    <property name="password" value="password"></property>
    </dataSource>
    </environment>
    </environments>
    
    <mappers>
    <!--
    使用资源的绝对路径
    <mapper url=""/>
    
    -->
    
    <!-- 资源的相对路径-->
    <mapper resource="User.xml"></mapper>
    <!--
    Mapper接口的全类名
    要求:Mapper接口的名称与映射文件名称一致且必须在同一个目录下
    <mapper class="com.qf.mapper.UserMapper"/>
    -->
    
    <!-- 加载某个包下的映射文件 (推荐)
    要求:Mapper接口的名称与映射文件名称一致且必须在同一个目录下
    
    <package name="com.qf.mapper"/>
    
    -->
    </mappers>
    
    </configuration>

    注:如果mapper 标签使用package标签,须在pom文件内加入以下代码

    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.xml</include>
    </includes>
    </resource>
    </resources>
    </build>

     

    3.编写SQL.xml文件(命名建议与dao接口一致)

    注:该文件有两种编写方式,主要区别在于mapper标签的namespace属性的值的设定,一种是自定义名字,一种是对应dao接口的全类名,分别对应调用时使用原生api调用和Mapper接口调用。

    原生api方式

    <?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="test">
    <select id="selectById" parameterType="java.lang.Integer" resultType="com.qf.domain.User">
    select * from user where id = #{id}
    </insert>
    </mapper>

     

    Mapper接口方式

    <?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="dao接口全类名">
    <select id="selectById" parameterType="java.lang.Integer" resultType="com.qf.domain.User">
    select * from user where id = #{id}
    </insert>
    </mapper>

    注:id必须和接口定义的方法名一致!

     

    4.调用SQL.xml

    调用sql对应也有两种方式,两种调用方式都需要首先加载主配置文件并创建一个SQLSession对象。

    //加载mybatis配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //使用sqlSessionFactoryBuild来创建一个sqlSessionFactory
    SqlSessionFactory sqFactroy = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //获取到sqlsession 进行调取api
    SqlSession sqlSession = sqFactroy.openSession();

     

    原生api方式

    //通过id来进行查询
    User user = sqlSession.selectOne("test.selectById", 37);
    System.out.println(user);

    注:通过namespace.id定位要执行的sql语句。

     

    Mapper接口方式

    //调取对应的接口
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.selectById2(38);
    System.out.println(user1);

    注:首先通过反射获取对应接口,然后调用接口方法。由于接口方法名和sql.xml文件内sql语句id一致,所以可以映射到对应的sql语句上。

    注:在实际开发过程中,Mapper接口方式使用的多一些。

     

    MyBatis进阶使用

     

    1.多参数应用

    有时候一个sql语句需要同时传多个基本类型参数,这时需使用多参数传递技术,共有三种方式。

    select  *  from  user  limit  #{offset}, #{pagesize}

     

    使用Map传递

    接口:

    List<User> selectUserByPage(Map<String, Object> paramList);

    调用:

    @Test
    public void testSelectUserByPage(){
    
      Map<String, Object> map = new HashMap<String, Object>();
      map.put("offset", 0);
      map.put("pagesize", 2);
    
      UserMapper userDao = session.getMapper(UserMapper.class);
      List<User> userList = userDao.selectUserByPage(map);
    
      for (int i = 0; i < userList.size(); i++) {
        System.out.println(userList.get(i));
      }
    }

    注:map的key必须和sql语句占位符名字一致!

     

    使用注解方式

    接口:

    List<User> selectUserByPage(@Param(value="offset")int offset, @Param(value="pagesize")int pagesize);

    调用:

    @Test
    public void testSelectUserByPage(){
    
      UserMapper userDao = session.getMapper(UserMapper.class);
      List<User> userList = userDao.selectUserByPage2(0, 2);
    
      for (int i = 0; i < userList.size(); i++) {
        System.out.println(userList.get(i));
      }
    }

    注:注解的参数名必须和sql语句的占位符名字一致!

     

    使用序号方式

    sql语句:

    select * from author limit #{0}, #{1}

    接口:

    List<User> selectUserByPage(int offset, int pagesize);

    调用:

    @Test
    public void testSelectUserByPage(){
    
      UserMapper userDao = session.getMapper(UserMapper.class);
      List<User> userList = userDao.selectUserByPage3(1, 1);
    
      for (int i = 0; i < userList.size(); i++) {
        System.out.println(userList.get(i));
      }
    }

    注:接口中参数的位置顺序必须和sql语句中的占位符编号一致!

     

    2.延迟加载

    开发中表之间的关联十分常见,在查询数据时经常需要查询多张表的集合数据,但这些所有表的数据却并不是同时需要展示给用户的。

    如用户表同时关联了个人信息以及订单,查询时正常是将用户表、信息表和订单表内同一用户所有信息查询成一个集合,但此时的功能场景是只查询订单不查询个人信息,如果全部查询出来是属于浪费资源的行为。延迟加载就是这类问题的解决方案,开启延迟加载后,程序首先只查询用户表,当程序需要个人信息或订单时再去进行查询和加载,节省系统开销。

     

    开启延迟加载

    MyBatis默认未开启延迟加载,须手动开启。在主配置文件内添加如下代码:

    <settings>
    <!-- 启用延迟加载特性,不配置默认关闭该特性-->
    <setting name="lazyLoadingEnabled" value="true"></setting>
    <!-- 按需加载: false:使用关联属性,及时加载;true,加载对象,则加载所有属性-->
    <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    注:个人认为aggressiveLazyLoading设置为false才是真的延迟加载,因为设为true还是会查询多表(执行多个sql语句),设为false是需要从表数据时才执行sql语句。

     

    编写实体类

    从表

    public class UserLogin {
      private int id;
      private String userName;
      private String password;
    }

    主表

    public class UserDetail {
      private int id;
      private String nickName;
      private String sex;
      private String address;
      private Date birthday;
      private int userId;
      private UserLogin userLogin;  // 从表对象
      private List<Orders> ordersList;  // 从表对象
    }

     

    编写sql.xml

    主表

    <resultMap id="resultBase" type="com.qf.domain.UserDetail">
      <id column="id" property="id"></id>
      <result column="nick_name" property="nickName"></result>
      <result column="sex" property="sex"></result>
      <result column="address" property="address"></result>
      <result column="birthday" property="birthday"></result>
      <result column="user_id" property="userId"></result>
      <association property="userLogin" select="com.qf.dao.UserLoginMapper.findById" column="user_id"></association>
    </resultMap>
    
    <select id="findAll" resultMap="resultBase">
      select * from user_detail
    </select>

    从表

    <select id="findById" resultMap="resultBase" parameterType="int">
      select * from user_login where id=#{id}
    </select>

    注:user_id是主表与从表的关联字段,对应从表的id字段。

     

    调用:

    @Test
    public void lazyLoadTest(){
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserDetailMapper mapper = sqlSession.getMapper(UserDetailMapper.class);
      List<UserDetail> userDetails= mapper.findAll();
      for (UserDetail userDetail:userDetails){
        System.out.println(userDetail.getId());
      }
      sqlSession.close();
    }

    注:调用处若aggressiveLazyLoading设为true,即使不打印UserLogin的数据也会执行查询UserLogin的SQL语句;若设为false,则会在打印(调用)UserLogin的数据时才会执行查询UserLogin的SQL语句。

     

    3.嵌套映射

     MyBatis的resultMap标签是一个非常强大的标签,当实体类属性名和数据库字段名不一致时,可以使用resultMap进行映射;实际项目中数据库中的表一般都不会单独存在,一般都会存在一对一或一对多的关联关系,使用resultMap标签可以使实体类互相嵌套映射关系。MyBatis中有三种嵌套映射关系:嵌套查询、嵌套结果、嵌套集合。

     

    嵌套查询

    嵌套查询类似延迟加载,sql.xml写法几乎一样,即在一个查询语句里嵌套另一个对象的查询语句,只不过延迟加载会根据对象数据是调用时机延迟执行查询语句,而嵌套查询是一口气把嵌套的所有sql与执行完毕。

    sql.xml文件

    <resultMap id="resultObj" type="com.qf.domain.UserDetail">
      <id column="id" property="id"></id>
      <result column="nick_name" property="nickName"></result>
      <result column="sex" property="sex"></result>
      <result column="address" property="address"></result>
      <result column="birthday" property="birthday"></result>
      <result column="user_id" property="userId"></result>
      <association property="userLogin" select="com.qf.dao.UserLoginMapper.findById" column="user_id">
        <id column="id" property="id"></id>
        <result column="username" property="userName"></result>
        <result column="password" property="password"></result>
      </association>
    </resultMap>
    
    <select id="findById" resultMap="resultObj">
      select * from user_detail where id = #{id}
    </select>

    接口:

    UserDetail findById(int id);

     

    嵌套结果

    嵌套结果与嵌套查询不同的是,嵌套结果是执行一条SQL语句查询多张表,即表连接查询,返回结果是多张表数据的集合;嵌套查询是执行多条SQL语句分别查询对应的表,返回结果同样在一个实体类对象里。

    SQL.xml文件

    <resultMap id="resultObj" type="com.qf.domain.UserDetail">
      <id column="id" property="id"></id>
      <result column="nick_name" property="nickName"></result>
      <result column="sex" property="sex"></result>
      <result column="address" property="address"></result>
      <result column="birthday" property="birthday"></result>
      <result column="user_id" property="userId"></result>
      <association property="userLogin" resultMap="userLogin"></association>
    </resultMap>
    
    <resultMap id="userLogin" type="userLogin">
      <id column="id" property="id"></id>
      <result column="username" property="userName"></result>
      <result column="password" property="password"></result>
    </resultMap>
    
    <select id="findById" resultMap="resultObj">
      select * from user_detail a,user_login b where a.user_id = b.id and a.id= #{id}
    </select>

    接口:

    UserDetail findById(int id);

     

    嵌套集合

    嵌套集合的应用场景是一对多的表关联结构,如一位用户有多个订单记录,此时查询的结果就需要返回一个用户和一个订单集合。

    SQL.xml文件

    <resultMap id="resultList" type="com.qf.domain.UserDetail">
      <id column="id" property="id"></id>
      <result column="nick_name" property="nickName"></result>
      <result column="sex" property="sex"></result>
      <result column="address" property="address"></result>
      <result column="birthday" property="birthday"></result>
      <result column="user_id" property="userId"></result>
    
      <collection property="ordersList" ofType="com.qf.domain.Orders" javaType="java.util.ArrayList">
    
        <id property="ordersid" column="ordersid" />
        <result property="orederUserId" column="orederUserId" />
        <result property="number" column="number" />
        <result property="createtime" column="createtime" />
        <result property="note" column="note" />
      </collection>
    </resultMap>
    
    <select id="findById" resultMap="resultList">
      select a.address,a.nick_name,a.id,a.sex,a.birthday,a.user_id,b.id as ordersid,b.createtime,b.number,b.user_id as orederUserId from user_detail a,orders b where a.user_id = b.user_id and a.id= #{id}
    </select>

    注:property用于指定在Java实体类是保存集合关系的属性名称;javaType用于指定在Java实体类中使用什么类型来保存集合数据,多数情况下这个属性可以省略的;ofType用于指定集合中包含的类型。

    接口:

    UserDetail findById(int id);

     

    4.动态SQL

    MyBatis的动态sql可以改善在提交数据时,如果只更新部分字段,导致其他字段被设置为null的情况,因为MyBatis提交引用类型是以对象形式提交的,若只更新部分字段,该对象没有set数据的属性默认为null,一旦执行update方法,会导致其余字段变成null的情况;还可以解决查询时不确定查询条件的,如图书管理系统,用户可能根据书名、出版社、作者其中之一或全部条件进行查询,如果不使用动态SQL,一般解决办法是判断参数是否为空,调用对应的SQL语句,这样的话同一功能需要编写多个SQL语句,极大降低开发效率。MyBatis的动态SQL主要有:if、choose(when、otherwize)、trim、where、set、foreach。

     

    if元素

    示例:解决不确定查询条件的查询功能

    <select id="dynamicIfTest" parameterType="Blog" resultType="Blog">
      select * from t_blog where 1 = 1
      <if test="title != null">
         and title = #{title}
      </if>
      <if test="content != null">
        and content = #{content}
      </if>
      <if test="owner != null">
        and owner = #{owner}
      </if>
    </select>

     

    示例:解决更新数据其他字段为null

    <update id="dynamicIfTest" parameterType="Blog" resultType="Blog">
      update user set
      <if test="username != null">
        user_name = #{username}
      </if>
      <if test="password != null">
        and pass_word = #{password}
      </if>
    </update>

     

    choose元素

    choose-when-otherwize元素类似于java的switch-case-default语句。

    <select id="dynamicChooseTest" parameterType="Blog" resultType="Blog">
      select * from t_blog where 1 = 1 
      <choose>
        <when test="title != null">
          and title = #{title}
        </when>
        <when test="content != null">
          and content = #{content}
        </when>
        <otherwise>
          and owner = "owner1"
        </otherwise>
      </choose>
    </select>

     

    where元素

    where元素就是代替了SQL语句中的where关键字,在需要使用where关键词的地方使用where元素,不用担心元素内是否会有错误代码,where元素会自动的剔除多于的and、or和增加空格。

    <select id="dynamicWhereTest" parameterType="Blog" resultType="Blog">
      select * from t_blog 
      <where>
        <if test="title != null">
          title = #{title}
        </if>
        <if test="content != null">
          and content = #{content}
        </if>
        <if test="owner != null">
          and owner = #{owner}
        </if>
      </where>
    </select>

     

    set元素

    set元素功能和where元素类似,替换SQL语句中的set关键字,并且自动的格式化SQL语句,使其能够正常执行。

    <update id="dynamicSetTest" parameterType="Blog">
      update t_blog
      <set>
        <if test="title != null">
          title = #{title},
        </if>
        <if test="content != null">
          content = #{content},
        </if>
        <if test="owner != null">
          owner = #{owner}
        </if>
      </set>
      where id = #{id}
    </update>

     

    trim元素

    trim是更灵活用来去处多余关键字的标签,它可以用来实现 where 和 set 的效果。

    示例:trim代替where元素

    <select id="getusertlist_if_trim" resultmap="resultmap_user"> 
      select * from user u
      <trim prefix="where" prefixoverrides="and|or"> 
        <if test="username !=null "> 
          u.username like concat(concat('%', #{username, jdbctype=varchar}),'%') 
        </if> 
        <if test="sex != null and sex != '' "> 
          and u.sex = #{sex, jdbctype=integer} 
        </if> 
        <if test="birthday != null "> 
          and u.birthday = #{birthday, jdbctype=date} 
        </if>
      </trim> 
    </select>

     

    示例:trim代替set元素

    <update id="updateuser_if_trim" parametertype="com.qf.pojo.user"> 
      update user
      <trim prefix="set" suffixoverrides=","> 
        <if test="username != null and username != '' "> 
          username = #{username}, 
        </if> 
        <if test="sex != null and sex != '' "> 
          sex = #{sex}, 
        </if> 
        <if test="birthday != null "> 
          birthday = #{birthday}, 
        </if>
      </trim> 
      where user_id = #{user_id} 
    </update>

    注:trim可以更灵活的控制SQL语句的结构,但个人认为没啥实际意义。

     

    forEach元素

    forEach元素主要应用在SQL语句中使用in关键字时,它可以在SQL语句中进行迭代一个集合,其主要属性有:

    • item表示集合中每一个元素进行迭代时的别名
    • index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置
    • open表示该语句以什么开始
    • separator表示在每次进行迭代之间以什么符号作为分隔 符
    • close表示以什么结束
    • collection表示传入的参数类型,list对应集合,array对应数组,map对应Map

    集合类型

    SQL.xml文件

    <select id="dynamicForeachTest" resultType="Blog">
      select * from t_blog where id in
      <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
        #{item}
      </foreach> 
    </select>

    调用

    @Test
    public void dynamicForeachTest() {
      SqlSession session = Util.getSqlSessionFactory().openSession(); 
      BlogMapper blogMapper = session.getMapper(BlogMapper.class);
      List ids = new ArrayList();
      ids.add(1);
      ids.add(3);
      ids.add(6);
      List blogs = blogMapper.dynamicForeachTest(ids);
      for (Blog blog : blogs){
        System.out.println(blog);
    
      }
    }

     

    数组类型

    SQL.xml文件

    <select id="dynamicForeach2Test" resultType="Blog">
      select * from t_blog where id in
      <foreach collection="array" index="index" item="item" open="(" separator="," close=")">
        #{item}
      </foreach>
    </select>

    调用

    @Test
    public void dynamicForeach2Test() {
      SqlSession session = Util.getSqlSessionFactory().openSession();
      BlogMapper blogMapper = session.getMapper(BlogMapper.class);
      int[] ids = new int[] {1,3,6,9};
      List blogs = blogMapper.dynamicForeach2Test(ids);
      for (Blog blog : blogs){}
        System.out.println(blog); 
      }
    }

     

    Map类型

    SQL.xml文件

    <select id="dynamicForeach3Test" resultType="Blog">
      select * from t_blog where title like "%"#{title}"%" and id in
      <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
        #{item}
      </foreach>
    </select>

    调用

    @Test
    public void dynamicForeach3Test() {
      SqlSession session = Util.getSqlSessionFactory().openSession();
      BlogMapper blogMapper = session.getMapper(BlogMapper.class);
      List ids = new ArrayList();
      ids.add(1);
      ids.add(2);
      ids.add(3);
      Map params = new HashMap();
      params.put("ids", ids);
      params.put("title", "中国");
      List blogs = blogMapper.dynamicForeach3Test(params);
      for (Blog blog : blogs){
        System.out.println(blog);
    
      }
    }

     

    5.MyBatis使用注解模式

    MyBatis对应成增删改查有如下注解:

    @Insert:实现新增
    @Update:实现更新
    @Delete:实现删除
    @Select:实现查询
    @Result:实现结果集封装
    @Results:可以与@Result 一起使用,封装多个结果集
    @ResultMap:实现引用@Results 定义的封装

    但是注解对于复杂的SQL语句心有余而力不足,使用注解实现简单的增删改查效率还是挺高的。

  • 相关阅读:
    List集合
    ArrayList_toArray
    Collection集合基础知识
    Array类的使用
    16.10
    16.9
    16.8
    16.7
    16.6
    16.5
  • 原文地址:https://www.cnblogs.com/lee-yangyaozi/p/11040801.html
Copyright © 2020-2023  润新知