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 [[email protected]]
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 [[email protected]]
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 [[email protected]]
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的下拉列表做筛选。

mybatis中many2one

时间: 2024-12-15 13:55:21

mybatis中many2one的相关文章

mybatis中one2many

上一章讲了many2one,接下来看看单向的one2many,请注意,下面的代码只做演示,不推荐在真实项目中使用.通过这个例子,也能更深刻的理解到在使用mybatiis的时候,应该更加谨慎的设计对象. 首先创建对象: public class Employee {     private Long id;     private String name; } public class Department {     private Long id;     private String nam

2017年9月3日 Spring及Mybatis中连接数据库的不同方式

连接数据库用spring和mybatis中使用的方法可以不同,mybaits可以不用写数据库的配置文件 Spring的连接方法 <!-- 读取属性文件(.properties)的内容 --> <!-- location:指定要读取的属性文件的位置及文件名. 注: classpath:表示依据类路径去查找 容器依据路径读取属性文件的内容, 并且将这些内容存放到Properties对象上 --> //数据库的登入数据文件 //文件名db.properties #db connectio

mybatis中&quot;#&quot;和&quot;$&quot;的区别

mybatis中"#"和"$"的区别 动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析.mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}. 在下面的语句中,如果 username 的值为 zhangsan,则两种方式无任何区别: select * from user where name = #{name}; select * from

mybatis中的mapper接口文件以及example类的实例函数以及详解

##Example example = new ##Example(); example.setOrderByClause("字段名 ASC"); //升序排列,desc为降序排列. example.setDistinct(false)//去除重复,boolean型,true为选择不重复的记录. Criteria criteria = new Example().createCriteria(); is null;is not null; equal to(value);not equ

MyBatis中如何通过继承SqlSessionDaoSupport来编写DAO(一)

在MyBatis中,当我们编写好访问数据库的映射器接口后,MapperScannerConfigurer就能自动成批地帮助我们根据这些接口生成DAO对象(),然后我们再使用Spring把这些DAO对象注入到业务逻辑层的对象(Service类的对象).因此,在这种情况下的DAO层,我们几乎不用编写代码,而且也没有地方编写,因为只有接口.这固然方便,不过如果我们需要在DAO层写一些代码的话,这种方式就无能为力了.此时,MyBatis-Spring提供给我们的SqlSessionDaoSupport类

MyBatis中的OGNL教程

MyBatis中的OGNL教程 有些人可能不知道MyBatis中使用了OGNL,有些人知道用到了OGNL却不知道在MyBatis中如何使用,本文就是讲如何在MyBatis中使用OGNL. 如果我们搜索OGNL相关的内容,通常的结果都是和Struts有关的,你肯定搜不到和MyBatis有关的,虽然和Struts中的用法类似但是换种方式理解起来就有难度. MyBatis常用OGNL表达式 e1 or e2 e1 and e2 e1 == e2,e1 eq e2 e1 != e2,e1 neq e2

myBatis中的注解@Param、返回值为Map、JAVA读取Excel并解析文本、Class.getResource()和ClassLoader.getResource()

myBatis中的注解@Param:http://blog.csdn.net/gao36951/article/details/44258217:  http://www.cnblogs.com/thomas12112406/p/6217211.html. myBatis返回值为Map:http://blog.csdn.net/werewr342352321df/article/details/11892755. ====================== JAVA读取Excel并解析文本:h

mybatis中xml的sql之test中文报错

在mybatis中sql,test中文报错( java.lang.NumberFormatException 这句话明确告诉了我们是数字格式异常).需加.tostring(). <if test="bookName == '毛选集'.tostring() "> and b.book_Name like #{bookName} </if>

Mybatis中的resultType和resultMap 区别

Mybatis中的resultType和resultMap 是mybatis 中返回类型一定用到的,但不会同时出现.mybatis返回类型肯定是map结构,然后根据返回类型是map还是对象类型,再转换. 在给对象设置属性的时候,两个方法肯定会调用. private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader