• mybatis中many2one


    上一章我们讲到,如果说hibernate是面向对象为主,关系为辅,那么在mybatis中则是着重考虑的是关系模型,换句话说,如果对象模型设计的不好,就会很容易的感觉到实现的难度。

    首先来看看最简单的单向many2one:
    建立对象:
    public class Customer {
        private Long id;
        private String name;
    }

    public class Orders {
        private Long id;
        private String sn;
        private Double price;
        private Customer customer;
    }
    很简单,Orders和Customer是一个单向的many2one的关系。和hibernate一样,我们先来配置简单的customer:
    创建CustomerMapper.xml:
    <mapper namespace="cd.itcast.mybatis.customer">
        <insert id="save" keyProperty="id" parameterType="Customer"
        useGeneratedKeys="true">
            INSERT INTO customer(name) values (#{name})
        </insert>

        <select id="get" resultType="Customer" parameterType="int">
            SELECT * FROM customer WHERE id = #{id}
        </select>
    </mapper>
    只是一个很简单的单对象映射操作。
    在hibernate的many2one中我们知道,在many方的外键关联到one方的主键。在many方保存的时候,应该在对应外键保存关联one方的id。所以,many方的保存insert应该是这样:
    OrdersMapper.xml:
    <insert id="save" keyProperty="id" parameterType="Orders"
    useGeneratedKeys="true">
        INSERT INTO orders(sn,price,customer_id) values (#{sn},#{price},#{customer.id})
    </insert>
    在这里看到,我们在对应外键(customer_id)的地方,使用#{customer.id}来代表orders的customer属性的id属 性,mybatis会像EL那样自动帮我们得到值。所以,在这里一定要注意保存对象的顺序,必须是先保存one再保存many方。在hibernate 中,如果保存顺序有误,hibernate会在提交事务的时候同步脏数据,帮我们补一条update语句来保证对象的关系正确,但是在mybaits中, 所有的sql都由我们来完成,所以没人会帮我们来完成update。所以,对应的保存测试应该是:
    @Test
    public void testSave(){
        SqlSession session=MyBatisUtil.openSession();

        Customer c=new Customer();
        c.setName("itcasT");

        Orders o=new Orders();
        o.setPrice(800d);
        o.setSn("001");
        o.setCustomer(c);

        session.insert("cd.itcast.mybatis.customer.save",c);
        session.insert("cd.itcast.mybatis.orders.save",o);

        session.commit();
        session.close();
    }

    下面就是many方的获取了。如果只是简单的把orders配置成一个单对象,即使用resultType来映射:
    <select id="get" parameterType="long" resultType="Orders">
        SELECT * FROM orders WHERE o.id = #{id}
    </select>
    如果是这样,在测试的时候:
    @Test
    public void testGet(){
        SqlSession session=MyBatisUtil.openSession();
        Orders o=session.selectOne("cd.itcast.mybatis.orders.get", 1l);
        System.out.println(o);
        Customer c=o.getCustomer();
        System.out.println(c);
        session.close();
    }
    会看到,orders对应的customer是null,只能说明mybatis不能帮我们自动完成相关的关系映射。下面就来看下在mybatis中的关系映射:
    第一种方式:使用额外的sql:
    <select id="get" parameterType="int" resultMap="ordersmap">
         SELECT * FROM orders WHERE o.id = #{id}
    </select>
    在这里,添加resultMap来完成相关的映射:
    <resultMap type="Orders" id="ordersmap">
        <id property="id" column="id"/>
        <result property="sn" column="sn"/>
        <result property="price" column="price"/>
        <association property="customer" column="customer_id" javaType="Customer" 
        select="cd.itcast.mybatis.customer.get">
    </resultMap>
    可能第一次看这个配置会觉得很奇怪,其实很好理解:
    1,association:在mybatis中不像hibernate那样,把关系分的很细致,比如one-to-one,many-to-one,而 只是把关系分成了两种:第一种是对单对象的关系,就用association,一种是集合的关系,这个待会再看。因为这里orders只是把 customer对象作为自己的一个属性,所以使用association来表示这个关系;
    2,property:和hibernate一样,定义这个关系在orders对象上面的对应属性。在完成关联对象的映射后,会使用这个属性把对象设置到orders对象上;
    3,column:代表从查询结果集(select * from orders where id = ?)中得到对关系维护的列,这里就指明的是外键customer_id;
    4,javaType:代表完成映射后应该返回的对象类型,这里肯定就是Orders
    5,select:是这种映射方式的最关键的地方,这个select指代着cd.itcast.mybatis.customer.get这条SQL,这 条sql即是得到customer的sql,而这条sql需要传入一个customer的id,我们前面已经通过 column="customer_id"指定了应该传给cd.itcast.mybatis.customer.get的值就是结果集中的 customer_id。
    所以,可以看出,mybatis在这种映射的方式为:首先执行SELECT * FROM orders WHERE o.id = #{id},然后对 id,sn,price属性直接从结果集中得到;接着,从结果集中得到customer_id列对应的值,这个值即是orders对应customer对 象的id,然后把这个值作为id交给cd.itcast.mybatis.customer.get去执行,而这个执行的结果会被拼装为一个 Customer对象,接着这个对象再会被设置到orders的customer属性中。完成映射。
    再次执行get测试:
    DEBUG [main] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@19b1de]
    DEBUG [main] - ==>  Preparing: SELECT * FROM ORDERS WHERE id = ? 
    DEBUG [main] - ==> Parameters: 1(Long)
    TRACE [main] - <==    Columns: id, sn, cust_id
    TRACE [main] - <==        Row: 1, 001, 1
    DEBUG [main] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@19b1de]
    DEBUG [main] - ==>  Preparing: SELECT * FROM CUSTOMER WHERE id = ? 
    DEBUG [main] - ==> Parameters: 1(Long)
    TRACE [main] - <==    Columns: id, name
    TRACE [main] - <==        Row: 1, c
    Orders [id=1, sn=001]
    Customer [id=1, name=c]
    可以看到,正常查询得到结果。但是,可能会有点疑问,我们之前在学hibernate的时候,不是说延迟加载是一种比较好的处理对象的方式么?但是现在我们看到例子中的对象并没有延迟加载。因为默认情况下,mybatis不会延迟加载对象:
    要开启延迟加载,需要给mybatis增加几个配置,在mybatis-config.xml中:
    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    再次执行get测试:得到的结果和刚才一样,仍然感觉没有执行延迟加载。问题在哪里呢?其实mybatis已经完成了延迟加载,只是mybatis的延迟 加载策略和hibernate的延迟加载策略不一样。Hibernate是把被加载的目标对象做成proxy对象提供延迟加载(即这里的 Customer),而mybatis是把对象本身做成延迟加载对象,我们把get测试修改一下:
    SqlSession session = MyBatisUtil.getInstance().openSession();
    OrdersMapper om=session.getMapper(OrdersMapper.class);
    Orders o=om.get(1l);
    System.out.println(o.getClass());
    session.close();
    打印结果:
    DEBUG [main] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@19b1de]
    DEBUG [main] - ==>  Preparing: SELECT * FROM ORDERS WHERE id = ? 
    DEBUG [main] - ==> Parameters: 1(Long)
    TRACE [main] - <==    Columns: id, sn, cust_id
    TRACE [main] - <==        Row: 1, 001, 1
    class cd.itcast.mybatis.domain.Orders$$EnhancerByCGLIB$$39a00050
    001
    可以看到,orders对象却是是proxy对象,并且访问其他属性,不会发送sql。但是,只要得到customer,那么查询customer的 sql就会立刻发送。简单来说,mybatis的延迟加载原则是,只要你去拿这个对象,表明你在用这个对象。注意和hibernate的区别。

    第二种方式,也是mybatis推荐的方式。上面那种方式,容易产生N+1问题。所以,mybatis建议使用内联的映射方式。修改ordersmapping:
    <resultMap type="Orders" id="ordersmapper">
        <id property="id" column="id"/>
        <result property="sn" column="sn"/>
        <association property="customer" javaType="Customer">
            <id property="id" column="cid"/>
            <result property="name" column="cname"/>
        </association>
    </resultMap>
    <select id="get" resultMap="ordersmapper" parameterType="long">
        SELECT c.id as cid,c.name as cname,o.* FROM
    ORDERS o LEFT JOIN CUSTOMER c ON o.CUST_ID = o.id WHERE o.id = #{id}
    </select>
    第一,在select中使用left join的方式把orders和对应的customer一起查询出来,并且为customer设置对应的别名。
    第二,修改resultMap,去掉select和column,而修改为内部的映射形式。
    那么,当mybatis执行完成sql之后,会把响应的属性转换成Orders对象的属性,当解析到customer属性时,mybatis会使用内部的映射关系把结果集中对应的列转换成Customer对象。
    再次运行get,执行结果相同。

    再次说明,mybatis建议使用第二种方式来完成映射。有人又会考虑到,那么如果只是orders的下拉列表,在结果里面不需要customer,这条 sql不是很浪费性能么?这里又要说到mybatis的另一个原则,按需配置。意思是,如果只是用作下拉列表,那么只需要再做一个select来专门为 orders的下拉列表做筛选。

  • 相关阅读:
    Ubuntu 各版本的几个国内更新源
    input只读属性 设置和移除 选择数字
    安装mysql驱动之 mysqlclient 出现的报错处理(ubuntu16.04)
    ubuntu16.04 安装 python3.6, 并创建虚拟环境(使用python3.6)
    python tornado 中使用 flash消息闪现
    ubuntu查看mysql版本的几种方法
    Ubuntu 下查看已安装的软件
    python ORM
    3、MongoDB的使用
    2、MongoDB安装
  • 原文地址:https://www.cnblogs.com/shenming/p/3812470.html
Copyright © 2020-2023  润新知