MyBatis学习笔记

PO, BO,VO,POJO的关系和区别

PO:persistent object 持久对象

1 .有时也被称为Data对象,对应数据库中的entity,可以简单认为一个PO对应数据库中的一条记录。

2 .在hibernate持久化框架中与insert/delet操作密切相关。

3 .PO中不应该包含任何对数据库的操作。

POJO :plain ordinary java object 无规则简单java对象

一个中间对象,可以转化为PO、DTO、VO。

1 .POJO持久化之后==〉PO

(在运行期,由Hibernate中的cglib动态把POJO转换为PO,PO相对于POJO会增加一些用来管理数据库entity状态的属性和方法。PO对于programmer来说完全透明,由于是运行期生成PO,所以可以支持增量编译,增量调试。)

2 .POJO传输过程中==〉DTO

3 .POJO用作表示层==〉VO

PO 和VO都应该属于它。

BO:business object 业务对象

业务对象主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。

比如一个简历,有教育经历、工作经历、社会关系等等。我们可以把教育经历对应一个PO,工作经历对应一个PO,社会关系对应一个PO。

建立一个对应简历的BO对象处理简历,每个BO包含这些PO。

这样处理业务逻辑时,我们就可以针对BO去处理。

封装业务逻辑为一个对象(可以包括多个PO,通常需要将BO转化成PO,才能进行数据的持久化,反之,从DB中得到的PO,需要转化成BO才能在业务层使用)。

关于BO主要有三种概念

1 、只包含业务对象的属性;

2 、只包含业务方法;

3 、两者都包含。

在实际使用中,认为哪一种概念正确并不重要,关键是实际应用中适合自己项目的需要。

VO:value object 值对象 / view object 表现层对象

1 .主要对应页面显示(web页面/swt、swing界面)的数据对象。

2 .可以和表对应,也可以不,这根据业务的需要。

DTO(TO):Data Transfer Object 数据传输对象

1 .用在需要跨进程或远程传输时,它不应该包含业务逻辑。

2 .比如一张表有100个字段,那么对应的PO就有100个属性(大多数情况下,DTO内的数据来自多个表)。但view层只需显示10个字段,没有必要把整个PO对象传递到client,这时我们就可以用只有这10个属性的DTO来传输数据到client,这样也不会暴露server端表结构。到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO。

DAO:data access object数据访问对象

1 .主要用来封装对DB的访问(CRUD操作)。

2 .通过接收Business层的数据,把POJO持久化为PO。

简易的关系图:

MyBatis学习笔记

对于写配置文件的MyBatis来说,整个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">
<!--namespace:命名空间,用于隔离SQL语句,后继有重要作用-->
<!--动态代理开发规则
    1.namespace必须是接口的全路径名
    2.接口的方法名必须与SQL ID一致
    3.接口的入参必须与parameterType类型一致
    4.接口的返回值必须与resultType类型一致
-->
<!--
    id:语句的唯一标示
    parameterType:入参的数据类型
    resultType:返回结果的数据类型
    #{}:点位符,相当于jdbc的?,里面写个传入对象的属性名字就好
    ${}:字符串拼接指令,如果入参为普通数据类型,括号内只能写value
-->
<mapper namespace="cn.gaolei.mapper.OrderMapper">
    <!--resultMap的入门,type是映射的类的别名-->
<!--resultMap代表的是自己编写的一种返回类型,比如说他返回的
不是一种固定的对象映射,而是自己要拼接组合出来的一种类型,
那么就使用resultMap来配置,id是resultMap的名字,type是resultMap实际返回的类型-->
    <resultMap id="order_list_map" type="order">
        <!--配置主键,ID用于映射主键-->
<!--property是对应的类的属性名,而column是对应的列名-->
        <id property="id" column="id"/>
        <!--普通字段用result映射-->
        <result property="userId" column="user_id"/>
        <result property="number" column="number"/>
        <result property="createtime" column="createtime"/>
        <result property="note" column="note"/>
    </resultMap>
    <resultMap id="order_user_map" type="order">
        <id property="id" column="id"/>
        <result property="userId" column="user_id"/>
        <result property="number" column="number"/>
        <result property="createtime" column="createtime"/>
        <result property="note" column="note"/>
        <!--association用于配置一对一关系,property是要映射的属性,javaType是类的数据类型,支持别名(别名默认是类名的首字母小写),column
是数据库表的列名 select 表示执行一条查询语句,将查询到的数据封装到property所代表的类型对象上(当然这条语句也要自己写)-->
        <association property="user" javaType="cn.gaolei.pojo.User" column = "user_id" select = "selectUserWithId">
            <id property="id" column="user_id"/>
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <result property="sex" column="sex"/>
            <result property="birthday" column="birthday"/>
        </association>
    </resultMap>
    <!--使用resultmap,这条查询语句,对于接口中的方法来说,返回结果不是resultMap,而是一个list,list里面封装的是resultMap.-->
<!--关于一对多的封装,请看下一段代码-->
    <select id="getOrderListMap" resultMap="order_list_map">
        select * from `order`;
    </select>
<!--我们的select标签,同理还有insert, update, delete, sql, cache,
 cache-ref, resultMap 标签-->
<!--这里的id要和mapper接口里的方法名完全一致-->
<!--标签还会有其他参数,比如:parameterType 传入参数的完全限定名,这个参
数是可选的-->
<!--还有比如-->
<!--resultType  返回结果的类型,如果正好是个类的话,mybatis会自动把列
映射到类的属性上面去-->
    <select id="getOrderList" resultType="order">
        select * from `order`;
    </select>
    <select id="getOrderUser" resultType="orderuser">
        select
        o.id,o.number,o.createtime,o.note,u.username,u.address,u.id
        from `order` o left join `user` u on o.user_id=u.id;
    </select>
    <select id="getOrderUserMap" resultMap="order_user_map">
        select
    o.id,o.user_id,o.number,o.createtime,o.note,u.username,u.address,u.sex,u.birthday
        from `order` o left join `user` u on o.user_id=u.id;
    </select>
<!--sql标签就是一段代码段,id就是这段代码段的名字,$(prefix)是个占位符,使用这段代码段的时候,可以把占位符换成其他的内容.-->
<sql id = "userColumns">
${prefix}.id, ${prefix}.username, ${prefix}.address from 
</sql>
<!--使用sql片段,片段要用include标签来引用,refid的值就是你要引用的sql片段的id,property标签用来把sql片段中的占位符(name)换成实际的值(value)-->
<select>
    select 
    <include refid = "userColumns">
        <property name = "prefix" value = "user" />
    </include>
    `user`
</select>
</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="cn.gaolei.mapper.UserMapper">
    <resultMap id="user_order_map" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
        <!--一对多,用collection,property是对应的多的那一方的属性名,ofType是属性对
应的集合当中的类的名称,支持别名 javaType表示该属性对应的类型名称.
column 是表示使用Oid作为参数进行后面的select语句查询
select 是表示执行一条查询语句,将查询到的结果封装到property所代表的类型对象当中(当然
这个查询语句要自己写) fetchType表示的是加载类型,有立即加载,eager和延迟加载lazy.一般使
用lazy性能会提高.不过mybatis要打开相应的设置才可以.不然没用-->
        <collection property="orders" ofType="cn.gaolei.pojo.Order" javaType = "ArrayList"
column = "oid" select = "selectOrdersByOid" fetchType = "lazy">
            <!--配置主键,ID用于映射主键-->
            <id property="id" column="oid"/>
            <!--普通字段用result映射-->
            <result property="userId" column="user_id"/>
            <result property="number" column="number"/>
            <result property="createtime" column="createtime"/>
            <result property="note" column="note"/>
        </collection>
    </resultMap>
    <!--sql片段的抽取,定义-->
    <sql id="user_sql">
        select * from
    </sql>

    <select id="getUserById" parameterType="int" resultType="cn.gaolei.pojo.User">
        <!--引用定义好的SQL片段,见上方-->
<!--这里的#{}也是一种占位符,他会直接从你传入方法的参数里找.如果传入的是一个POJO,那么
他会找对象的对应名字的属性来映射上去-->
        <include refid="user_sql"/>
        user where id=#{id2};
    </select>
    <!--
        虽然返回的是集合,但是结果类型写单个元素的足矣
    -->
    <select id="getUserByUserName" parameterType="string" resultType="cn.gaolei.pojo.User">
        select * from user where username like '%${value}%';
    </select>

    <!--if标签的使用,if标签用于条件判断-->
    <select id="getUserByPojo" parameterType="user" resultType="cn.gaolei.pojo.User">
        select * from user
        <!--where标签自动补上where关键字,同时处理多余的and字符-->
        <where>
            <if test="username!=null and username!=''">
                and username like '%${username}%'
            </if>
            <if test="sex!=null and sex!=''">
                and sex=#{sex};
            </if>
        </where>
    </select>

    <!--新增数据,传入的参数是个对象类型-->
    <insert id="insertUser" parameterType="cn.gaolei.pojo.User">
        insert into user values(null,#{username},#{birthday},#{sex},#{address});
    </insert>

    <select id="getUserCount" resultType="int">
        select count(1) from user;
    </select>

    <select id="getUserByIds" parameterType="queryvo" resultType="user">
        <include refid="user_sql"/>
        user
        <where>
            <!--foreach循环标签的使用
                collection:要遍历的集合
                open:循环开始之前输出的内容
                close:循环开始之后输出的内容
                separator:设置分割符
                item:循环的变量
            -->
            <!--目的:组成id in (1,10,24)-->
            <foreach collection="ids" open="id in (" close=")" item="uID" separator=",">
                #{uID}
            </foreach>
        </where>
    </select>
    <select id="getUserOrderMap" resultMap="user_order_map">
        select u.id,u.username,u.birthday,u.address,u.sex ,
        o.id oid,o.number,o.createtime,o.note
        from user u left join `order` o on u.id = o.user_id;
    </select>
</mapper>

关于多对多的话,其实和一对多一样的.

MyBatis的动态sql,也就是sql片段,有很多种.常用的动态SQL元素包括:

if

<select id ="selectEmployeeByIdLike" resultType = "cn.gaolei.pojo.Employee">
    select * from employee where state = 'active'
<!--if就是一个可选条件,如果传进来的参数有id属性,那么就加上id查询条件,在
test里面写判断条件即可,判断条件可以用and或or来拼接-->
    <if test = "id != null">
        and id = #{id}
    </if>
</select>

choose(when、otherwise)

<select id = "selectEmployeeChoose" parameterType = "hashmap"(这些类型全小写也完全没问题的,别名不区分大小写) resultType = "cn.gaolei.pojo.Employee" >
    select * from employee where state = 'active'
<!--如果传入id,就根据id查询,没有传入id就根据loginname和password查询,否则查询sex等于男的数据-->
    <choose>
        <when test = "id != null">
            and id = #{id}
        </when>
        <when test = "loginname != null and password != null">
            and loginname = #{loginname} and password = #{password}
        </when>
        <otherwise>
            and sex = '男'
        </otherwise>
    </choose>
</select>

where

<select id = "selectEmployeeByIdLike" resultType = "cn.gaolei.pojo.Employee">
    select * from employee 
<!--where 标签很智能,如果里面没有一个为真,那么就没有where,而且多余的and也能智能帮你去除.--> 
    <where> 
        <if test = "state != null">
            state = #{state}
        </if>
        <if test = "id != null">
            and id = #{id}
        </if>
    </where>
</select>

set

<!--set元素可以用于动态包含需要更新的列,而不包含其他的列-->
<update id = "updateEmployeeIfNecessary" parameterType = "cn.gaolei.pojo.Employee">
    update employee
    <set>
        <if test = "loginname != null">loginname = #{loginname},</if>
        <if test = "password != null">password = #{password},</if>
    </set>
    where id = #{id}
</update>
<!--set元素会智能帮你消除无关的逗号.-->

foreach(对集合进行遍历)

<!--foreach指定一个集合,相当于把集合里的每一个元素都加入到括号里了.item就是要遍历的变量.当使用可迭代对象或者数组时,
index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。-->
<select id = "selectEmployeeIn" resultType = "cn.gaolei.pojo.Employee">
    select * from employee where id in
    <foreach item = "item" index = "index" collection = "list" open = "(" seperator = "," close = ")">
        #{item}
    </foreach>
</select>

bind

<select id = "selectEmployeeLikeName" resultType = "cn.gaolei.pojo.Employee">
<!--你可以把bind理解为创造一个变量,后面就可以直接使用了-->
    <bind name = "pattern" value = "'%' + _parameter.getName() + '%'"/>
    select * from employee where loginname like #{pattern}
</select>

MyBatis的注解配置

让mybatis告别xml,走上注解的高速公路~~~
在mapper接口文件的方法上直接写上各种注解即可.
@Select @Insert @Update @Delete
注解后面括号里跟的语句和在xml文件里写的一样就行.resultmap则可以使用@Results({@Result}). 举个例子:

@Select("select * from employee where id = #{id}")
@Results({
    @Result(id = true, column = "id", property = "id"),
    @Result(column = "name", property = "name"),
    @Result(column = "sex", property = "sex"),
    @Result(column = "age", property = "age")//如果列和属性的名称相同,还可以省略@Result注解
}) 
Employee selectEmployeeById(Integer id);

关于一对一关联的代码如下

public interface PersonMapper {
    @Select("select * from person where id = #{id}")
    @Results({
        @Result(id = true, column = "id", property = "id")
        @Result(column = "name", property = "name"),
        @Result(column = "sex", property = "sex"),
        @Result(column = "age", property = "age")   
        @Result(column = "card_id", property = "card",
        one = @One(
            select = "cn.gaolei.mapper.CardMapper.selectCardById", fetchType = FetchType.EAGER))
})//one属性表示是个一对一的关联列.select表示需要关联执行的sql语句.
//fetchtype表示加载类型是立即加载EAGER还是延迟加载LAZY
    Person selectPersonById(Integer id);
}

一对多关联的话,把一对一的one = @One() 改成 many = @Many()即可

注解的动态SQL

注解的动态sql有一些复杂,过程是这样的:

  1. 注解改为@SelectProvicer @InsertProvider @UpdateProvider @DeleteProvider
  2. 注解里的type写一个Provider类.class, method写对应的该类的方法.
  3. 新建Provider类(就是你第二步里用的那个),里面要有你第二步写的那个方法,同时该方法的参数和你注解的Mapper里面的方法的参数一样.
  4. 该方法要return new SQL(){具体逻辑}.toString();
  5. 写好具体逻辑,SELECT 、FROM、WHERE、JOIN、INNER_JOIN、LEFT_OUTER_JOIN、RIGHT_OUTER_JOIN、OR、AND、GROUP_BY、HAVING、ORDER_BY、INSERT_INTO、VALUES(要插入的列,要插入的值)、DELETE_FROM、UPDATE、SET都成为了这个SQL匿名内部类的方法。用这些方法。实现这里的你需要的方法就行了。
    举个例子:
public interface EmployeeMapper {
    @SelectProvider(Type = EmployeeDynaSqlProvider.class, method = "selectWithParam")
    List<Employee> selectWithParam(Map<String, Object> param);
} 

public class EmployeeDynaSqlProvider {
    public String selectWithParam(Map<String, Object> param) {
    return new SQL(){
        SELECT("*");
        FROM("employee");
        if (param.get("id") != null) {
            WHERE("id = #{id}");
        }
        //...再有其他的if
    }.toString;
    }
}

Mybatis使用分页助手PageHelper也非常的简单,
第一步,在pom文件中引入依赖:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>最新版本</version>
</dependency>

第二步,在要分页查询的方法前面加一个静态方法PageHelper.startPage(pageNum, pageSize);
第一个参数是第几页,第二个参数是每页的大小.那么查询结果就是自动分页的了.
但要注意,只有静态方法后面的第一个方法会被分页.要想再次分页,需要再次调用静态方法.

关于#{}和${}的区别如下:

#{ }:解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。
例如,Mapper.xml中如下的 sql 语句:

select * from user where name = #{name};
动态解析为:
select * from user where name = ?;
一个 #{ }被解析为一个参数占位符 ? 。
${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。

例如,Mapper.xml中如下的 sql:

select * from user where name = ${name};
当我们传递的参数为 "Jack" 时,上述 sql 的解析为:

select * from user where name = "Jack";
预编译之前的 SQL 语句已经不包含变量了,完全已经是常量数据了。 综上所得,${ }变量的替换阶段是在动态 SQL 解析阶段,而 #{ }变量的替换是在 DBMS 中。

上一篇:(十五)Appium+Python自动化测试PO模式


下一篇:树链剖分