MyBatis中,自动映射的三种模式:java
默认的自动映射模式为PARTIAL。
在MyBatis全局配置文件中,在setting标签内设置自动映射模式:sql
<!--全局配置参数--> <settings> <setting name="autoMappingBehavior" value="PARTIAL" /> </settings>
若是某些resultMap中不想使用自动映射,则能够单独在该resultMap中设置autoMapping属性为false:mybatis
<select id="findUserById" parameterType="Long" resultMap="UserResult" autoMapping="false"> select id,name,email from t_user where id=#{id} </select>
这里autoMapping属性会忽略全局配置文件中"outoMappingBehavior"映射模式。app
若是Java包装类使用驼峰命名规则,则要开启mapUnderscoreToCamelCase属性,让自动映射机制将SQL查询出的非驼峰命名方式的字段名与Java包装类中的属性进行自动映射:测试
<settings> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings>
根据一些查询条件来选择不一样的SQL语句:this
<select id="findUserList" parameterType="cn.com.mybatis.po.UserQueryVo" resultType="cn.com.mybatis.po.User"> select * from user <where> <if test="UserQuertVo != null"> <if test="UserQueryVo.gender != null and UserQueryVo.gender !=''"> and user.sex=#{UserQueryVo.gender} </if> <if test="UserQueryVo.username != null and UserQueryVo.username != ''"> and user.username like '%${UserQueryVo.username}%' </if> </if> </where> </select>
当使用"<where>"标签对包裹if条件语句时,将会忽略查询条件中的第一个"and"。spa
将复用性比较强的SQL语句封装成SQL片断,但在SQL片断中不支持动态SQL语句的<where>
标签:代理
<sql id="queryUserWhere"> <!--要复用的SQL语句--> </sql>
引用SQL映射配置:code
<select id="findUserList" parameterType="cn.com.mybatis.po.UserQueryVo" resultType="cn.com.mybatis.po.User"> select * from user <where> <include refid="queryUserWhere"></include> <!--在这里可能还要引用其余的SQL片断--> </where> </select>
除了自身所在的Mapper文件,每一个SQL映射配置还能够引用外部Mapper文件中的SQL片断,只须要在refid属性中填写SQL片断的id名添加所在Mapper文件的namespace信息便可。orm
有时查询语句可能包含多个查询信息,好比查询多个id的User用户:
select * from user where id in (2.4.6)
此时若是要在Mapper文件中配置这样的语句,这里定义一个Java包装类,其属性为一个包含多个id信息的List集合:
public class UserQueryVo{ private List<Integer> ids;//包含多个id信息 public List<Integer> getIds(){ return ids; } public void setIds(List<Integer> ids){ this.ids = ids; } }
在Mapper中配置一个SQL片断,并在查询SQL映射中引入它:
<sql id="queryUserWhere"> <if test="ids!=null"> <foreach collection="ids" item="user_id" open="and id in (" close=")" separator=","> #{user_id} </foreach> </if> </sql> <select id="findUserList" parameterType="cn.com.mybatis.po.UserQueryVo" resultType="cn.com.mybatis.po.User"> select * from user <where> <include refid="query_user_where"></include> </where> </select>
在SQL片断里的"and"用来拼接已有一个或多个查询条件的语句,当此语句为第一个查询条件时,会由于"<where>"的存在而屏蔽第一个"and"。
在实现一对一查询时,推荐使用resultType。
使用resultMap时,对映射输出的数据须要单独定义resultMap,过程有些繁琐,若是对查询结果有特殊的要求(好比JavaBean里面又包含有其余JavaBean)就可使用。
好比查询一个购买批次的信息以及建立该批次的用户,一个批次对应着一个用户。批次表名为batch。
首先建立批次表batch对应的Java实体类Batch:
package cn.com.mybatis.po; import java.util.Date; import java.util.List; public class Batch { private int batch_id; private int cus_id; private String number; private Date createtime; private String note; public int getBatch_id() { return batch_id; } public void setBatch_id(int batch_id) { this.batch_id = batch_id; } //其余的get和set方法省略...... }
建立一个以batch为父类,而后追加用户信息:
package cn.com.mybatis.po; public class BatchCustomer extends Batch { private String username; private String acno; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAcno() { return acno; } public void setAcno(String acno) { this.acno = acno; } }
而后在UserMapper.xml映射文件中定义select类型的查询语句:
<select id="findBatchCustomer" resultType="cn.com.mybatis.po.BatchCustomer"> SELECT batch.*,customer.username,customer.acno FROM batch INNER JOIN customer ON batch.cus_id = customer.cus_id </select>
编写测试方法:
@Test public void testBatchCustomer() throws Exception{ SqlSession sqlSession = dataConnection.getSqlSession(); List<BatchCustomer> batchCustomerList = sqlSession.selectList("test.findBatchCustomer"); if(batchCustomerList != null){ BatchCustomer batchCustomer = null; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (int i=0;i<batchCustomerList.size();i++){ batchCustomer = batchCustomerList.get(i); System.out.println("卡号为" + batchCustomer.getAcno() + "的名为" +batchCustomer.getUsername()+"的客户:\\n于"+sdf.format(batchCustomer.getCreatetime()) +"采购了批次号为"+batchCustomer.getNumber()+"的一批理财产品"); } } sqlSession.close(); }
使用resultMap能够映射实体类中包裹的其余实体类。
建立一个封装了批次属性和Customer实体类的BatchItem批次类:
package cn.com.mybatis.po; import java.util.Date; import java.util.List; public class BatchItem { private int batch_id; private int cus_id; private String number; private Date createtime; private String note; private Customer customer; public int getBatch_id() { return batch_id; } public void setBatch_id(int batch_id) { this.batch_id = batch_id; } }
SQL映射文件:
<!--一对一查询,使用resultMap实现--> <resultMap id="BatchInfoMap" type="cn.com.mybatis.po.BatchItem"> <id column="batch_id" property="batch_id"/> <result column="cus_id" property="cus_id"/> <result column="number" property="number"/> <result column="createtime" property="createtime" javaType="Date"/> <result column="note" property="note"/> <association property="customer" javaType="cn.com.mybatis.po.Customer"> <id column="cus_id" property="cus_id"/> <result column="username" property="username"/> <result column="acno" property="acno"/> <result column="gender" property="gender"/> <result column="phone" property="phone"/> </association> </resultMap> <select id="findBatchCustomerToMap" resultMap="BatchInfoMap"> SELECT batch.*,customer.username,customer.acno FROM batch INNER JOIN customer ON batch.cus_id = customer.cus_id </select>
编写测试类:
@Test public void testBatchCustomerToMap() throws Exception{ SqlSession sqlSession = dataConnection.getSqlSession(); List<BatchItem> batchItemList = sqlSession.selectList("test.findBatchCustomerToMap"); if(batchItemList != null){ BatchItem batchItem = null; Customer customer = null; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (int i=0;i<batchItemList.size();i++){ batchItem = batchItemList.get(i); customer = batchItem.getCustomer(); System.out.println("卡号为" + customer.getAcno() + "的名为" +customer.getUsername()+"的客户:\\n于"+sdf.format(batchItem.getCreatetime()) +"采购了批次号为"+batchItem.getNumber()+"的一批理财产品"); } } }
好比,查询一批中一个用户信息,和有哪些理财产品。
修改一下Batch类:
package cn.com.mybatis.po; import java.util.Date; import java.util.List; public class Batch { private int batch_id; private int cus_id; private String number; private Date createtime; private String note; //批次中包含的理财产品订购信息 private List<BatchDetail> batchDetials; //get和set方法省略...... }
理财产品的订购信息实体类以下:
package cn.com.mybatis.po; import java.util.List; public class BatchDetail { private int id; private int batch_id; private int product_id; private int product_num; private FinacialProduct finacialProduct; //get和set方法省略...... }
编写SQL配置,使用extends标签继承上面一对一查询中的名为BatchInfoMap的resultMap:
<!--一对多--> <resultMap id="BatchAndBatchDetailResultMap" type="cn.com.mybatis.po.BatchItem" extends="BatchInfoMap"> <collection property="batchDetails" ofType="cn.com.mybatis.po.BatchDetail"> <!-- id:订单明细的惟一标识 --> <id column="id" property="id"/> <result column="batch_id" property="batch_id"/> <result column="product_id" property="product_id"/> <result column="product_num" property="product_num"/> </collection> </resultMap> <select id="findBatchAndBatchDetail" resultMap="BatchAndBatchDetailResultMap"> SELECT batch.*, customer.username,customer.acno, batchdetail.product_id, batchdetail.product_num FROM ((batch INNER JOIN customer ON batch.cus_id = customer.cus_id) INNER JOIN batchdetail ON batch.batch_id = batchdetail.batch_id) </select>
测试查询结果:
@Test public void testfindBatchAndBatchDetail() throws Exception{ SqlSession sqlSession=dataConnection.getSqlSession(); //调用userMapper的方法 BatchItem batchItem= sqlSession.selectOne( "test" + ".findBatchAndBatchDetail"); if(batchItem!=null){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Customer customer = batchItem.getCustomer();//取出该批次的用户信息 //取出该批次订购的理财产品信息 List<BatchDetail> batchDetails = batchItem.getBatchDetails(); System.out.println("卡号为"+customer.getAcno()+"的名为" +customer.getUsername()+"的客户:\\n于" +sdf.format(batchItem.getCreatetime())+"采购了批次号为" +batchItem.getNumber()+"的一批理财产品,详情以下:"); BatchDetail batchDetail = null; if(batchDetails!=null){ for (int i = 0; i < batchDetails.size(); i++) { batchDetail = batchDetails.get(i); System.out.println("id为"+batchDetail.getProduct_id() +"的理财产品"+batchDetail.getProduct_num()+"份"); } } } sqlSession.close(); }
这里使用了association与collocation,一个映射单一实体对象,一个映射集合对象。
查询全部用户以及用户对应的批次订单中全部理财产品的详细信息。
修改后的Customer用户类,它包含用户信息以及用户下的全部批次信息:
package cn.com.mybatis.po; import java.io.Serializable; import java.util.List; public class Customer implements Serializable{ private int cus_id; private String username; private String acno; private String gender; private String phone; private List<Batch> batchList; //get和set方法省略 }
修改后的Batch批次信息类,它包含单个批次信息以及批次明细列表:
package cn.com.mybatis.po; import java.util.Date; import java.util.List; public class Batch { private int batch_id; private int cus_id; private String number; private Date createtime; private String note; private List<BatchDetail> batchDetials; //get和set方法省略 }
修改后的BatchDetail批次明细类,它包含单个批次明细和对应的理财产品引用:
package cn.com.mybatis.po; import java.util.List; public class BatchDetail { private int id; private int batch_id; private int product_id; private int product_num; private FinacialProduct finacialProduct; //get和set方法省略 }
新增的FinacialProduct产品明细类,包含理财产品的各类属性:
package cn.com.mybatis.po; import java.util.Date; public class FinacialProduct { private int id; private String name; private double price; private String detail; private String imgpath; private Date invattime; //get和set方法省略 }
编写Mapper映射文件:
<resultMap id="UserAndProductsResultMap" type="cn.com.mybatis.po.Customer"> <!-- 客户信息 --> <result column="username" property="username"/> <result column="acno" property="acno"/> <!--批次订单信息,一个客户对应多个订单--> <collection property="batchList" ofType="cn.com.mybatis.po.Batch"> <id column="batch_id" property="batch_id"/> <result column="cus_id" property="cus_id"/> <result column="number" property="number"/> <result column="createtime" property="createtime" javaType="java.util.Date"/> <result column="note" property="note"/> <collection property="batchDetials" ofType="cn.com.mybatis.po.BatchDetail"> <!-- id:订单明细的惟一标识--> <id column="id" property="id"/> <result column="batch_id" property="batch_id"/> <result column="product_id" property="product_id"/> <result column="product_num" property="product_num"/> <association property="finacialProduct" javaType="cn.com.mybatis.po.FinacialProduct"> <id column="product_id" property="id"/> <result column="name" property="name"/> <result column="price" property="price"/> <result column="detail" property="detail"/> </association> </collection> </collection> </resultMap> <select id="findUserAndProducts" resultMap="UserAndProductsResultMap"> SELECT BATCH.*, CUSTOMER.username, CUSTOMER.acno, BATCHDETAIL.product_id, BATCHDETAIL.product_num, FINACIAL_PRODUCTS.name, FINACIAL_PRODUCTS.detail, FINACIAL_PRODUCTS.price FROM BATCH, CUSTOMER, BATCHDETAIL, FINACIAL_PRODUCTS WHERE BATCH.cus_id = CUSTOMER.cus_id AND BATCHDETAIL.batch_id=BATCH.batch_id AND FINACIAL_PRODUCTS.product_id=BATCHDETAIL.product_id; </select>
在MyBatis中,一般会进行多表联合查询,可是有时候并不会当即用到全部的联合查询结果。这种“按需查询”的机制,就可使用延迟加载来实现。
开启延迟加载功能:
//在全局配置文件中配置setting属性 <configuration> <settings> <!--打开延迟加载的开关--> <setting name="lazyLoadingEnable" value="true" /> <!--将积极加载改成按需加载--> <setting name="aggressiveLazyLoading" value="false" /> </settings> </configuration>
例如查询全部批次订单的信息,而每一个批次订单中会关联查询用户,延迟加载用户信息,首先定义只查询全部批次订单信息的SQL配置:
<select id="findBatchUserLazyLoading" resultMap="BatchUserLazyLoadingResultMap"> select * from batch </select> <!--延迟加载的resultMap--> <resultMap id="BatchUserLazyLoadingResultMap" type="cn.com.mybatis.po.BatchItem"> <!--对订单信息进行映射配置--> <id column="batch_id" property="batch_id" /> <result column="cus_id" property="cus_id" /> <result column="number" property="number" /> <result column="createtime" property="createtime" javaType="java.util.Date" /> <result column="note" property="note" /> <!--实现延迟加载用户信息--> <association property="customer" javaType="cn.com.mybatis.po.Customer" select="findCustomerById" column="cus_id"> </association> </resultMap>
其中select用来指定Mapper.xml配置文件中的某个select标签对的id。
column是指订单信息中关联用户信息查询的列。
最后配置延迟加载要执行的获取用户信息的SQL:
<select id="findCustomerById" parameterType="int" resultType="cn.com.mybatis.po.Customer"> select * from customer where cus_id=#{id} </select>
其中输入参数就是association中column中定义的字段信息。
编写测试方法:
@Test public void testFindBatchCustomerLazyLoading() throws Exception{ SqlSession sqlSession=dataConn.getSqlSession(); //调用userMapper的方法,获取全部订单信息(未加载关联的用户信息) List<BatchItem> batchItemList=sqlSession.selectList("findBatchUserLazyLoading"); BatchItem batchItem = null; Customer customer = null; for (int i = 0; i < batchItemList.size(); i++) { batchItem = batchItemList.get(i); System.out.println("订单编号:"+batchItem.getNumber()); //执行getCustomer时才会去查询用户信息,这里实现了延迟加载 customer=batchItem.getCustomer(); System.out.println("订购用户姓名:"+customer.getUsername()); } sqlSession.close(); }
综上所述,使用延迟加载方法,先执行简单的查询SQL(最好查询单表,也能够关联查询),再按须要加载关联查询的其余信息。
新增一个CustomerMapper.xml,例如对Customer的增、删、改、查的SQL配置:
<?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.com.mybatis.mapper.CustomerMapper"> <!-- 查询用户 --> <select id="findCustomerById" parameterType="int" resultType="cn.com.mybatis.po.Customer"> SELECT * FROM CUSTOMER WHERE cus_id=#{cus_id} </select> <!-- 新增用户 --> <insert id="insertCustomer" parameterType="cn.com.mybatis.po.Customer"> INSERT INTO CUSTOMER(username,acno,gender,phone) value(#{username},#{acno},#{gender},#{phone}) </insert> <!-- 删除用户 --> <delete id="deleteCustomer" parameterType="java.lang.Integer"> DELETE FROM CUSTOMER WHERE cus_id=#{cus_id} </delete> <!-- 修改用户 --> <update id="updateCustomerAcNo" parameterType="cn.com.mybatis.po.Customer" > UPDATE CUSTOMER SET acno = #{acno} WHERE cus_id=#{cus_id} </update> </mapper>
其中,namespace中的路径为即将建立的mapper代理接口的路径。
使用CustomerMapper.xml的Mapper代理,建立CustomerMapper接口:
package cn.com.mybatis.mapper; import cn.com.mybatis.po.Customer; public interface CustomerMapper { public Customer findCustomerById(int id) throws Exception; public void insertCustomer(Customer customer) throws Exception; public void deleteCustomer(int id) throws Exception; public void updateCustomerAcNo(Customer customer) throws Exception; }
测试动态代理效果:
@Test public void testFindCustomerOnMapper() throws Exception{ SqlSession sqlSession=dataConn.getSqlSession(); //获取Mapper代理 CustomerMapper customerMapper=sqlSession.getMapper(CustomerMapper.class); //执行Mapper代理对象的查询方法 Customer customer=customerMapper.findCustomerById(1); System.out.println("用户姓名:"+customer.getUsername()+"|" +"卡号:"+customer.getAcno()); sqlSession.close(); }
其中SqlSession类的getMapper方法的原理就是,根据Mapper代理接口的类型及Mapper.xml文件的内容,建立一个Mapper接口的实现类,其中实现了具体的增删改查方法。