使用Mybatis实现数据的增、删、改以及复杂查询

mybatis直接与数据库交互,实现了数据操作层,对数据库的操作主要有:增加、删除、更改、查询以及存储过程调用等操作。

一、数据操纵(增、删、改、查)

1、新增数据、更改数据传递的参数均为记录类型,mapper配置文件内容如下:

<insert id="insert" parameterType="com.springmvc.entity.SUser" >
    <!--
      WARNING - @mbggenerated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Wed Jan 17 11:25:43 CST 2018.
    -->
  <!--表示插入之后查询相应的插入数据,无需实现相应操作时可删除-->
 <selectKey resultType="java.lang.String" keyProperty="personid" order="AFTER" >
      SELECT SCOPE_IDENTITY()
    </selectKey>
    insert into S_USER (LOGINNAME, PASSWORD, UNTID, ROLEID)
    values (#{loginname,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{untid,jdbcType=VARCHAR}, 
      #{roleid,jdbcType=CHAR})
  </insert>
<update id="updateUser" parameterType="com.springmvc.entity.SUser">
        update S_USER set LOGINNAME=#{loginname,jdbcType=VARCHAR},PASSWORD=#{password,jdbcType=VARCHAR},UNTID=#{untid,jdbcType=VARCHAR},ROLEID=#{roleid,jdbcType=CHAR} WHERE PERSONID = #{personid,jdbcType=VARCHAR}
  </update>
2、删除数据

3、一般数据查询

4、复杂查询

mybatis复杂 查询sql主要有一下几类:

1)、通过if判断动态组织sql;

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
<!--通过条件判断动态生成sql-->
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

2)、通过choose (when,otherwize) (相当于java 语言中的 switch ,与 jstl 中的choose 很类似)动态组织sql;

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

3)、trim前缀、后缀组织复杂sql;

<select id="getUsertList_if_trim" resultMap="resultMap_User">  
    SELECT * 
      FROM user u
<!--使用trim能够有效避免在依次进行条件判断,组合查询条件时出现where后无条件或者where 后直接连接 and符合等问题-->
<!--有trim根据是否有条件确定是否在查询中增加where,也根据条件组合情况自动添加 条件连接符号(and 或者 or)-->
 <trim prefix="WHERE" prefixOverrides="AND|OR">  
        <if test="username !=null ">  
            u.username LIKE CONCAT(CONCAT('%', #{username, jdbcType=VARCHAR}),'%')  
        </if>  
        <if test="sex != null and sex != '' ">  
            AND u.sex = #{sex, jdbcType=INTEGER}  
        </if>  
        <if test="birthday != null ">  
            AND u.birthday = #{birthday, jdbcType=DATE}  
        </if>
    </trim>     
</select>
<!--trim还可以用于update中,以动态生成数据更改内容和条件-->
<update id="updateUser_if_trim" parameterType="com.yiibai.pojo.User">  
    UPDATE user  
    <trim prefix="SET" suffixOverrides=",">  
        <if test="username != null and username != '' ">  
            username = #{username},  
        </if>  
        <if test="sex != null and sex != '' ">  
            sex = #{sex},  
        </if>  
        <if test="birthday != null ">  
            birthday = #{birthday},  
        </if>           
    </trim>  
    WHERE user_id = #{user_id}  
</update>  

4)、foreach组织复杂sql;

oreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。
foreach元素的属性主要有 item,index,collection,open,separator,close。
    item表示集合中每一个元素进行迭代时的别名,
    index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,
    open表示该语句以什么开始,
    separator表示在每次进行迭代之间以什么符号作为分隔 符,
    close表示以什么结束。

在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的,主要有一下3种情况:
    1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
    2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
    3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在breast里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key 下面分别来看看上述三种情况的示例代码:

<select id="dynamicForeachTest" parameterType="java.util.List" resultType="Blog">
           select * from t_blog where id in
        <foreach collection="list" index="index" item="item" open="(" separator="," close=")">
                #{item}       
        </foreach>    
 </select>
其中list是调用dynamicForeachTest方法时需要传递的调用参数,当参数类型是"java.util.List"时传递list对象;为array时传递数组对象;否则传递每个元素对象类型为Map的list或arra

二、mybatis调用存储过程(特别复杂的查询完全可以通过存储过程实现,然后通过mybatis调用存储过程)

假设在数据库中以编写了存储过程:SP_MF_I_IMP;该存储过程的调用参数:

I_MANIFEST_I_ID    IN NUMBER , 
I_FUNCTION_CODE  IN VARCHAR2, 
I_FIRM_ID        IN NUMBER, I_OP_USER        IN VARCHAR2, --操作人ID O_RETURN_MESSAGE OUT VARCHAR2
 四个输入参数,一个输出参数;该存储过程执行成功后将返回类型为string的提示信息;该存储过程的xml配置信息如下:

<parameterMap id="sendMap" type="java.util.HashMap">    
    <parameter property="I_MANIFEST_I_ID" jdbcType="NUMERIC" javaType="java.lang.Long" mode="IN"/>    
    <parameter property="I_FUNCTION_CODE" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN"/>    
    <parameter property="I_FIRM_ID" jdbcType="NUMERIC" javaType="java.lang.Integer" mode="IN"/>    
    <parameter property="I_OP_USER" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN"/>  
    <parameter property="O_RETURN_MESSAGE" jdbcType="VARCHAR" javaType="java.lang.String" mode="OUT"/>    
</parameterMap>  
  
<update id="sendManifstmanToEdi" parameterMap="sendMap" statementType="CALLABLE">  
    <![CDATA[ 
        {call SP_MF_I_IMP(?, ?, ?, ?, ?)}   
    ]]>  
</update> 
需依据调用参数定义相应的hashmap类型

Map<String,Object> params = new HashMap<String, Object>();  
    try {  
        params.put("I_MANIFEST_I_ID", id);   
        params.put("I_FUNCTION_CODE", Constants.MAINIFEST_DECLARE);   
        params.put("I_FIRM_ID", getFirmOfLoginUser().getFirmId());   
        params.put("I_OP_USER", getLoginUser().getLoginName());  
        params.put("O_RETURN_MESSAGE", "");  
        manifestIMainService.sendManifstmanToEdi(params);  
        result = String.valueOf(params.get("O_RETURN_MESSAGE"));  
    } catch (Exception e) {  
        log.error("sendEdi occurred error.", e);  
        if(Constants.IS_TEST)  
            setErrorMsg(e.getMessage());  
    }  
    return result;  
当通过存储过程执行查询操作,返回打开的游标结果集时,需根据打开的游标集字段名称和类型,定义结果集,配置文件如下:

<resultMap id="flowLogsResultMap" type="com.hwt.glmf.log.vo.FlowLogModel">  
    <result column="ID" property="id" />  
    <result column="STATUS" property="status" />  
    <result column="OP_USER" property="op_user" />  
    <result column="OP_TIME" property="op_time" />  
    <result column="BILL_NO" property="bill_no" />  
    <result column="TRAN_NO" property="tran_no" />  
    <result column="MEMO" property="memo" />  
    <result column="DATA_TYPE" property="data_type" />          
</resultMap>  
  
  
<!-- jdbcType=INTEGER  ORACLE:INT-->  
<select id="viewFlowLog" parameterType="java.util.Map" statementType="CALLABLE">  
    <![CDATA[  
        {call SP_TRACE_GET(#{I_DATA_TYPE,mode=IN,jdbcType=VARCHAR},  
        #{I_TRAN_NO,mode=IN,jdbcType=VARCHAR},#{I_BILL_NO,mode=IN,jdbcType=NUMERIC},  
        #{O_LIST,mode=OUT,jdbcType=CURSOR,javaType=java.sql.ResultSet,resultMap=flowLogsResultMap})}  
    ]]>  
</select>  
其中:#{I_DATA_TYPE,mode=IN,jdbcType=VARCHAR},  #{I_TRAN_NO,mode=IN,jdbcType=VARCHAR},#{I_BILL_NO,mode=IN,jdbcType=NUMERIC}为调用存储过程输入参数的名称和类型,
#{O_LIST,mode=OUT,jdbcType=CURSOR,javaType=java.sql.ResultSet,resultMap=flowLogsResultMap}为打开游标输出的结果集
待用存储过程的代码如下:
public List<FlowLogModel> viewFlowLog(String dataType, String tran_no,  
        String bill_no) throws Exception {  
        Map<String, Object> param = new HashMap<String, Object>();         
            param.put("I_DATA_TYPE", dataType);  
            param.put("I_TRAN_NO", tran_no);  
            param.put("I_BILL_NO", bill_no);  
            param.put("O_LIST", OracleTypes.CURSOR);  
            flowLogDao.viewFlowLog(param);  
    return (List<FlowLogModel>)param.get("O_LIST");  


 
 

 
 关于mybatis访问存储过程可参见http://blog.csdn.net/wangchangpen62/article/details/44961983 
 

三、mybatis事务处理

集成了spring的mybatis一般在spring的mybatis配置文件中定义事务管理器,一般采用将mybatis参与到spring事务的方法进行事务处理;

1、配置文件加载顺序:spring+mvc+mybatis开发中一般会涉及到applicationContext.xml、mybatis.xml以及spring-mvc.xml几个配置文件;一般情况下在web.xml中首先定义

<listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
即:上下文监听器,以保证webApplicationContext能够自动创建持久层实现对象(即自动创建Dao接口相应对象,开发中无需实现该接口代码),然后创建通过context-param参数装载的applicationContext.xml、mybatis.xml配置文件; 在加载mybatis-context.xml时,若在mybatis.xml文件中定义了与业务和路由控制相关对象,Spring就会装配@Controlle和@Service的容器,并把他们放到Spring的容器中来;接着加载spring-mvc.xml,若在spring-mvc.xml文件中也定义了Cotroller、Service对象,这时Spring也会把@Service和@Controller注解的实例装配到Spring容器中,但是这时由于先前在加载mybatis-context.xml时已经装配过@Service和@Controller的容器,所以这时新装配的容器会覆盖掉先前的容器,所以Spring容器中就只剩下后来装配的容器了。

这种装配顺序就会引来一个问题,由于我的事务是在mybatis-context.xml文件中声明的,所以这个文件中的Service容器是带有事务能力的;但是这里装配的Service容器会被后来application-context.xml中的Service容器替换掉,而application-context.xml中的Service容器是没有事务能力的,所以最后造成在Spring容器中的Service是没有事务能力的。
这是很容易就陷入的一个陷阱;很多初学者在照着网上的教程配置好后,却发现无论如何都不能回滚或者其他事务的问题,很多时候就是由于这种情况造成的。
要解决这个问题,只需只在mybatis-context.xml中生成Service的带事务的容器,而在application-context.xml中就不生成Service容器了。又由于Service是Controller类中的一个属性,所以在装配Controller前要先装配好Service。
详细的处理过程可参见网文:http://blog.csdn.net/liujan511536/article/details/48002885
一般将事务控制放置到业务层,即:注释为@service的对象中,示例代码:

    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)  
    public void saveUser(User u) throws Exception{  
        userMapper.insert(u);  
              
        throw new Exception("hehe");  
              
    }  
注意:执行方法异常时需要回滚事务,需要设置@Transactional的属性rolllbackFor=Exception.class,即异常时回滚事务。
2、注解式的事务处理方法:

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

事务注解的详细介绍参见:

https://www.cnblogs.com/yepei/p/4716112.html




  





猜你喜欢

转载自blog.csdn.net/u012846041/article/details/79136423
今日推荐