mybatis知识点总结

原创
2018/08/16 07:15
阅读数 68

前言:

mybatis可以说是最容易上手的持久层框架了,相比于hibernate 而言,它都是直接用sql语句对数据库进行操作,而不是用hql,尤其是关联关系复杂的时候,mybatis更容易实现。下面是本人学习过程的一些笔记。

 本文涉及知识点:

1、mybatis入门 

2、配置版CRUD 

3、关联查询(1:1&1:n) 

4、mybatis 的注解形式 

5、sql语句构建器版CRUD 

6、动态sql的应用

一、mybatis入门

1、引入相关的依赖: 这里不作介绍,可以自行百度。

2、关于mybatis-config.xml:

    
  
  
  
  1. <?xml version="1.0" encoding="UTF-8" ?>

  2.  <!DOCTYPE configuration

  3.    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

  4.       "http://mybatis.org/dtd/mybatis-3-config.dtd">

  5.       <configuration>

  6.        <!-- 引入jdbc.properties配置文件,获取连接数据库信息,推荐 -->

  7.    <properties resource="jdbc.properties"/>

  8.    <!-- 连接数据库信息也可以直接写这里,如以下写法,不推荐 -->

  9.    <!-- <properties>       <property name="jdbc.driverClassName" value="com.mysql.jdbc.Driver"/>

  10.        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/db_mybatis"/>

  11.        <property name="jdbc.username" value="root"/>

  12.        <property name="jdbc.password" value="123456"/>

  13.    </properties> -->

  14.    <!-- 取别名,在StudentMapper.xml的parameterType属性中就不用写全类名 -->

  15.    <!-- 方式一,不推荐 -->

  16.    <!-- <typeAliases>

  17.        <typeAlias alias="Student" type="com.java1234.model.Student"/>

  18.    </typeAliases> -->

  19.    <!-- 方式二,推荐,扫描此包下所有实体 -->

  20.    <typeAliases>

  21.        <package name="com.zhu.entity"/>

  22.    </typeAliases>

  23.    <!-- 配置环境 -->

  24.    <environments default="development">

  25.        <environment id="development">

  26.            <transactionManager type="JDBC" />

  27.            <dataSource type="POOLED">

  28.                <property name="driver" value="${jdbc.driverClassName}" />

  29.                <property name="url" value="${jdbc.url}" />

  30.                <property name="username" value="${jdbc.username}" />

  31.                <property name="password" value="${jdbc.password}" />

  32.            </dataSource>

  33.        </environment>

  34.        <environment id="test">

  35.            <transactionManager type="JDBC" />

  36.            <dataSource type="POOLED">

  37.                <property name="driver" value="${jdbc.driverClassName}" />

  38.                <property name="url" value="${jdbc.url}" />

  39.                <property name="username" value="${jdbc.username}" />

  40.                <property name="password" value="${jdbc.password}" />

  41.            </dataSource>

  42.        </environment>

  43.    </environments>

  44.    <!-- 读取映射文件 -->

  45.    <mappers>

  46.        <!-- <mapper resource="com/zhu/mappers/StudentMapper.xml" /> 不推荐-->

  47.        <!-- <mapper class="com.zhu.mappers.StudentMapper"/> 不推荐-->

  48.        <!-- 推荐,扫描此包下所有映射文件 -->

  49.        <package name="com.zhu.mappers"/>

  50.    </mappers>

  51. </configuration>

以上大部分配置在做ssm整合时都会写在spring 配置文件中,了解有哪些配置即可。

3、dao层以及实现:

    
  
  
  
  1. public interface StudentMapper {

  2.      public int add(Student student);

  3.        }

StudentMapper.xml:

    
  
  
  
  1. <mapper namespace="com.zhu.mappers.StudentMapper">

  2.  <insert id="add" parameterType="Student">

  3.         insert into t_student 

  4.         values(null,#{name},#{age})

  5. </insert>

  6. </mapper> 

再来个获取sqlSessionFactory的工具类:

    
  
  
  
  1. public class SqlSessionFactoryUtil {

  2.    private static SqlSessionFactory

  3.                sqlSessionFactory;    

  4.    public static SqlSessionFactory 

  5.               getSqlSessionFactory(){

  6.               if(sqlSessionFactory==null){

  7. InputStream inputStream=null;

  8.    try{                             inputStream    =    Resources

  9. .getResourceAsStream("mybatisconfig.xml");

  10. sqlSessionFactory=new 

  11. SqlSessionFactoryBuilder().build(inputStream);

  12.    }catch(Exception e){

  13.        e.printStackTrace();

  14.        }

  15.             }

  16.         return sqlSessionFactory;

  17.             }  

  18.          public static SqlSession openSession(){

  19.        return getSqlSessionFactory()

  20.                .openSession();

  21.          }

  22.             }

4、测试:

    
  
  
  
  1. public static void main(String[] args) {

  2.        SqlSession sqlSession=SqlSessionFactoryUtil.openSession();

  3.        StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class);

  4.        Student student=new Student("李四",11);

  5.        int result=studentMapper.add(student);

  6.  sqlSession.commit();

二、配置版CRUD:

1、dao接口定义CRUD方法:

    
  
  
  
  1. public interface StudentMapper {

  2.    public int add(Student student);

  3.    public int update(Student student);

  4.    public int delete(Integer id);

  5.    public Student findById(Integer id);

  6.    public List<Student> find();

  7.       }

2、StudentMapper.xml: 

增加方法,id是接口中对应的方法名,parameterType就是方法的参数类型,因为在mybatis-config配置文件中配置了typeAliases,所以这里可以直接写Student,否则就要写全类名。 mybatis用 #{属性名} 获取实体类的属性值。

    
  
  
  
  1. <insert id="add" parameterType="Student"  >

  2.   insert into t_student 

  3.   values(null,#{name},#{age})

  4. </insert>

更新方法:

    
  
  
  
  1. <update id="update" parameterType=

  2.      "Student">

  3.     update t_student 

  4.     set name=#{name},age=#{age} 

  5.     where id=#{id}

  6. </update>

删除方法:

    
  
  
  
  1. <delete id="delete" parameterType="Integer">

  2.        delete from t_student 

  3.        where id=#{id}

  4. </delete>

根据id查学生(1个):resultType是参数类型

    
  
  
  
  1. <select id="findById" parameterType=

  2.        "Integer" resultType="Student">

  3.    select * from t_student 

  4.        where id=#{id}

  5. </select>

查询所有学生:可以先定义一个resultMap来接收返回的学生。

    
  
  
  
  1. <resultMap type="Student"

  2.                                 id="StudentResult">

  3.    <id property="id" column="id"/>

  4.        <result property="name" 

  5.                     column="name"/>

  6.        <result property="age"

  7.                     column="age"/>

  8. </resultMap>

在这里引用刚才定义的resultMap:

    
  
  
  
  1. <select id="find" resultMap=

  2.                          "StudentResult">

  3.    select * from t_student

  4. </select>

注意: 这里说一说resultType和resultMap,其实刚才那个方法也可以用resultType,如果查询结果字段与实体类字段名称完全一致,可以不映射;resultMap只有在select中才会用到。

有三种情况会用到ResultMap:

1、如果查询结果字段与实体类的字段名称不对应;

2、查询结果的字段类型与实体类的字段类型不一致;

3、查询结果对应多个实体类(多表关联查询时)。

三、多表关联查询问题

1、一对一关联:

 给student类增加一个address属性,一个学生对应一个地址

    
  
  
  
  1. public class Student {

  2.    private Integer id;

  3.    private String name;

  4.    private Integer age;

  5.    private Address address;

  6. }

    
  
  
  
  1. public class Address {

  2.    private Integer id;

  3.    private String sheng;

  4.    private String shi;

  5.    private String qu;

  6. }

根据student的id查询学生,查询结果带有地址信息

    
  
  
  
  1. public interface StudentMapper {

  2.    public Student

  3.        findStudentWithAddress(Integer id);

  4. }

AddressMapper.xml

    
  
  
  
  1. <select id="findById" parameterType="Integer" resultType="Address">

  2.        select * from t_address 

  3.                where id=#{id}

  4. </select>

StudentMapper.xml: 因为student新增了一个Address类型的属性,查询结果对应了student和Address两个类,属于复合类型,因此要定义resultMap来接收。

    
  
  
  
  1. <resultMap type="Student" id="StudentResult">

  2.        <id property="id" column="id"/>

  3.        <result property="name" column="name"/>

  4.        <result property="age" column="age"/>

  5.                 <--property表示实体类中的属性名,column是数据库中对应的字段名-->

  6. <association property="address"                   column="addressId"

  7.    select="com.zhu.mappers

  8. .AddressMapper.findById">

  9. </association>

  10. </resultMap>

然后在select标签中引用这个resultMap:

    
  
  
  
  1. <select id="findStudentWithAddress" resultMap="StudentResult" parameterType="Integer">

  2.          select * from t_student t1,t_address t2

  3. where t1.addressId=t2.id 

  4. and t1.id=#{id}

  5. </select>

2、一对多关联:

 一对多,多个学生属于一个年级,即学生为n,年级为1

    
  
  
  
  1. public interface StudentMapper {

  2.    public Student findByGradeId(Integer gradeId);

  3. }

StudentMapper.xml:

    
  
  
  
  1. <resultMap type="Student" id="StudentResult">

  2.        <id property="id" column="id"/>     <result property="name" column="name"/>

  3.    <result property="age" column="age"/>

  4.                  <!--property="grade"表示学生的grade属性,column="gradeId"表示外键,select指查询年级方法的全类名-->

  5.        <association property="grade" column="gradeId" select="com.zhu.mappers.GradeMapper.findById"></association>

  6.    </resultMap>

  7.         <select id="findByGradeId" resultMap="StudentResult" parameterType="Integer">

  8.    select * from t_student 

  9.        where gradeId=#{gradeId}

  10. </select>

    
  
  
  
  1. public interface GradeMapper {

  2.    public Grade findById(Integer id);

  3. }

GradeMapper.xml:

    
  
  
  
  1. <resultMap type="Grade" id="GradeResult">

  2.        <result property="id" column="id"/>

  3.    <result property="gradeName" column="gradeName"/>

  4.                 <!--property="students"表示grade的student属性,column="id",此ID是学生ID-->

  5.        <collection property="students" column="id" select="com.zhu.mappers.StudentMapper.findByGradeId"></collection>

  6. </resultMap>

  7. <select id="findById" parameterType="Integer" resultMap="GradeResult">

  8.        select * from t_grade 

  9.                where id=#{id}

  10. </select>

关于一对一以及一对多关联查询详解,请参考我的另一篇文章《mybatis的association以及collection的用法》。

四、mybatis的注解形式

mybatis也可以把sql语句直接把注解形式写在dao层的接口方法上: 

1、注解版CRUD:

    
  
  
  
  1. @Insert("insert into t_student values(null,#{name},#{age})")

  2.    public int insertStudent(Student student);

  3.    @Update("update t_student set name=#{name},age=#{age} where id=#{id}")

  4.    public int updateStudent(Student student);

  5.    @Delete("delete from t_student where id=#{id}")

  6.    public int deleteStudent(int id);

  7.    @Select("select * from t_student where id=#{id}")

  8.    public Student getStudentById(Integer id);

  9.    @Select("select * from t_student")

  10.    @Results(

  11.            {

  12.                @Result(id=true,column="id",property="id"),

  13.                @Result(column="name",property="name"),

  14.                @Result(column="age",property="age")

  15.            }

  16.    )

  17.    public List<Student> findStudents();

2、注解版关联查询:

①、一对一关联查询: studentMapper.java

    
  
  
  
  1.  @Select("select * from t_student where gradeId=#{gradeId}")

  2.    @Results(

  3.            {

  4.                @Result(id=true,column="id",property="id"),

  5.                @Result(column="name",property="name"),

  6.                @Result(column="age",property="age"),

  7.                @Result(column="addressId",property="address",one=@One(select="com.zhu.mappers.AddressMapper.findById"))

  8.            }

  9.    )

  10.    public Student selectStudentByGradeId(int gradeId);

gradeMapper.java

    
  
  
  
  1. public interface GradeMapper {

  2.    @Select("select * from t_grade where id=#{id}")

  3.    @Results(

  4.            {

  5.                @Result(id=true,column="id",property="id"),

  6.                @Result(column="gradeName",property="gradeName"),

  7.                @Result(column="id",property="students",many=@Many(select="com.java1234.mappers.StudentMapper.selectStudentByGradeId"))

  8.            }

  9.    )

  10.    public Grade findById(Integer id);

  11. }

②、一对一和一对多:

    
  
  
  
  1. @Select("select * from t_student where id=#{id}")

  2.    @Results(

  3.            {

  4.                @Result(id=true,column="id",property="id"),

  5.                @Result(column="name",property="name"),

  6.                @Result(column="age",property="age"),

  7.                @Result(column="addressId",property="address",one=@One(select="com.zhu.mappers.AddressMapper.findById")),

  8.                @Result(column="gradeId",property="grade",one=@One(select="com.zhu.mappers.GradeMapper.findById"))

  9.            }

  10.    )

  11.    public Student selectStudentWithAddressAndGrade(int id);

五、sql语句构建器版CRUD:

sql语句构建器,就是把sql语句写在一个类中,然后在接口方法上引用这个类,请看下面的代码: 

1、构建器类:

    
  
  
  
  1. public class StudentDynaSqlProvider {

  2.    public String insertStudent(final Student student){

  3.        return new SQL(){

  4.            {

  5.                INSERT_INTO("t_student");

  6.                if(student.getName()!=null){

  7.                    VALUES("name", "#{name}");

  8.                }

  9.                if(student.getAge()!=null){

  10.                    VALUES("age", "#{age}");

  11.                }

  12.            }

  13.        }.toString();

  14.    }

  15.    public String updateStudent(final Student student){

  16.        return new SQL(){

  17.            {

  18.                UPDATE("t_student");

  19.                if(student.getName()!=null){

  20.                    SET("name=#{name}");

  21.                }

  22.                if(student.getAge()!=null){

  23.                    SET("age=#{age}");

  24.                }

  25.                WHERE("id=#{id}");

  26.            }

  27.        }.toString();

  28.    }

  29.    public String deleteStudent(){

  30.        return new SQL(){

  31.            {

  32.                DELETE_FROM("t_student");

  33.                WHERE("id=#{id}");

  34.            }

  35.        }.toString();

  36.    }

  37.    public String getStudentById(){

  38.        return new SQL(){

  39.            {

  40.                SELECT("*");

  41.                FROM("t_student");

  42.                WHERE("id=#{id}");

  43.            }

  44.        }.toString();

  45.    }

  46.    public String findStudents(final Map<String,Object> map){

  47.        return new SQL(){

  48.            {

  49.                SELECT("*");

  50.                FROM("t_student");

  51.                StringBuffer sb=new StringBuffer();

  52.                if(map.get("name")!=null){

  53.                    sb.append(" and name like '"+map.get("name")+"'");

  54.                }

  55.                if(map.get("age")!=null){

  56.                    sb.append(" and age="+map.get("age"));

  57.                }

  58.                if(!sb.toString().equals("")){

  59.                    WHERE(sb.toString().replaceFirst("and", ""));                  

  60.                }

  61.            }

  62.        }.toString();

  63.    }

  64. }

2、在接口方法上引用:

    
  
  
  
  1. public interface StudentMapper {

  2.    @InsertProvider(type=StudentDynaSqlProvider.class,method="insertStudent")

  3.    public int insertStudent(Student student);

  4.    @UpdateProvider(type=StudentDynaSqlProvider.class,method="updateStudent")

  5.    public int updateStudent(Student student);

  6.    @DeleteProvider(type=StudentDynaSqlProvider.class,method="deleteStudent")

  7.    public int deleteStudent(int id);

  8.    @SelectProvider(type=StudentDynaSqlProvider.class,method="getStudentById")

  9.    public Student getStudentById(Integer id);

  10.    @SelectProvider(type=StudentDynaSqlProvider.class,method="findStudents")

  11.    public List<Student> findStudents(Map<String,Object> map);

  12. }

3、junit测试:

    
  
  
  
  1. @Test

  2.    public void testInsert() {

  3.        Student student=new Student("琪琪",11);

  4.        studentMapper.insertStudent(student);

  5.        sqlSession.commit();

  6.    }

六、动态sql的应用:

1、mybatis带条件分页查询: 

mybatis分页并不难,只要传入rowIndex和pageSize,然后用limit语句即可;关于带条件查询,要查哪个对象就把条件封装成那个对象的一个实体,然后在xml中通过where标签解析出来即可。话不多说,看如下代码: User.java

    
  
  
  
  1. public class User {

  2.    private Integer userId;

  3.    private String userName;

  4.    private Integer age;

  5.    private Card card;//一个人一张身份证,1对1

  6.    private List<MobilePhone> mobilePhone;//土豪,多个手机,1对多

  7. }

Card.java

    
  
  
  
  1. public class Card {

  2.    private Integer cardId;

  3.    private String cardNum;//身份证号

  4.    private String address;//地址

  5. }

MobilePhone.java

    
  
  
  
  1. private Integer mobilePhoneId;

  2.    private String brand;//品牌

  3.    private double price;//价格

  4.    private User user;//主人

  5. }

UserDao.java

    
  
  
  
  1. /**

  2.     * 带条件分页查询:

  3.     *     可输入的条件:名字(模糊),cardId,age,

  4.     * @param userCondition 把条件封装成一个user对象

  5.     * @param rowIndex 表示从第几行开始取数据

  6.     * @param pageSize 表示要返回多少行数据

  7.     * @return 返回user列表

  8.     */

  9.    List<User> queryUserList(@Param("userCondition") User userCondition, @Param("rowIndex") int rowIndex,

  10.        @Param("pageSize") int pageSize);

UserDao.xml

    
  
  
  
  1. <!--定义resultMap-->

  2. <resultMap type="User" id="userMap">

  3.           <id property="userId" column="user_id"/>

  4.           <result property="userName" column="user_name"/>

  5.           <result property="age" column="age"/>

  6.           <association property="card" column="card_id" javaType="Card">

  7.             <id property="cardId" column="card_id"/>

  8.            <result property="cardNum" column="card_num"/>

  9.            <result property="address" column="address"/>

  10.           </association>

  11.           <collection property="mobilePhone" column="user_id" ofType="MobilePhone">

  12.             <id column="mobile_phone_id" property="mobilePhoneId" />

  13.            <result column="brand" property="brand" />

  14.            <result column="price" property="price" />

  15.           </collection>

  16.     </resultMap>

  17. <!--带条件分页查询-->

  18. <select id="queryUserList" resultMap="userMap">

  19.         SELECT

  20.         u.user_name,u.age,u.card_id,c.card_num,c.address,m.brand,m.price

  21.         FROM

  22.         tb_user u,tb_card c,tb_mobile_phone m

  23.         <where>

  24.            <if

  25.                test="userCondition.card != null

  26.                     and 

  27.                    userCondition.card.cardId != null">

  28.                and u.card_id =

  29.                #{userCondition.card.cardId}

  30.            </if>

  31.            <if test="userCondition.userName != null">

  32.                and u.user_name like '%${userCondition.userName}%'

  33.            </if>

  34.            <if test="userCondition.age != null">

  35.                and u.age = #{userCondition.age}

  36.            </if>

  37.            AND

  38.            u.card_id = c.card_id

  39.            AND

  40.            u.user_id = m.user_id

  41.        </where>

  42.        LIMIT #{rowIndex},#{pageSize}

  43.     </select>

junit测试:

    
  
  
  
  1. @Test

  2.    public void testQueryUserList() {

  3.        User userCondition = new User();

  4.        /*Card c = new Card();

  5.        c.setCardId(2);

  6.        userCondition.setCard(c);*/

  7.        //userCondition.setAge(22);

  8.        userCondition.setUserName("菲");

  9.        List<User> ul = userDao.queryUserList(userCondition, 1, 99);

  10.        for(User user : ul) {

  11.            System.out.println(user.getUserName());

  12.            /*List<MobilePhone> list = new ArrayList<>();

  13.            list = user.getMobilePhone();

  14.            for(MobilePhone mp : list) {

  15.                System.out.println(mp.getBrand());

  16.            }*/

  17.        }

  18.    }

以上代码便完成了带条件分页查询,整个过程并不难,只是在select中用了where标签以及用where的子标签if判断传入的条件是否为空,不为空就赋值。

2、mybatis的动态更新: 

上面CRUD案例中的更新,name和age必须传入值,没有传入的话那就会更新成null或0,而这里所说的动态更新就是传了值的才更新,没传值的字段保留原来的值。看如下案例(实体类同带条件分页查询的三个实体类): UserDao.java

    
  
  
  
  1. int updateUser(User user);

UserDao.xml

    
  
  
  
  1. <!-- 更新user -->

  2.    <update id="updateUser" parameterType="User">

  3.         UPDATE tb_user

  4.         <set>

  5.            <if test="userName!=null">user_name=#{userName},</if>

  6.            <if test="age!=null">age=#{age},</if>

  7.            <if test="card!=null">card_id=#{card.cardId},</if>

  8.        </set>

  9.        where user_id=#{userId}

  10.    </update>

junit测试:

    
  
  
  
  1. @Test

  2.    public void testUpdateUser() {

  3.        User user = new User();

  4.        user.setUserId(4);

  5.        user.setAge(22);

  6.        Card c = new Card();

  7.        c.setCardId(1);

  8.        user.setCard(c);

  9.        int result = userDao.updateUser(user);

  10.        assertEquals(1, result);

  11.    }

动态更新就是在select标签中添加了一个set,然后再用if去判断传入的字段是否为空,不为空就更新。

总结:

mybatis相对于hibernate来说,优点在于支持sql语句,而hibernate使用的是hql语句。在业务逻辑简单不需要编写很多hql语句时可能使用hibernate更加快捷,因为它封装了一些对数据库的基本操作比如save、update等,直接调用就行;当业务逻辑比较复杂,那就选用mybatis更好。


本文分享自微信公众号 - java开发那些事(javawebkf)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部