美文网首页
mybatis输入映射、输出映射、动态sql

mybatis输入映射、输出映射、动态sql

作者: Eugene1024 | 来源:发表于2018-12-28 11:40 被阅读21次

    mybatis比较重要的就是输入映射和输出映射了

    一 、输入映射

    输入映射可以是简单数据类型,可以是包装类也可以是hashmap,下面主要分享下包装类

    应用场景:用于复杂的综合查询的时候,比如商城中查询订购某个商品的用户有多少
    ,就需要构建我们的包装类,用于组装我们的查询条件,传入到对应statement中执行综合查询sql

    实现下这个简单的例子,项目采用的是mapper代理模式开发,如果不懂的可以查看我的写的mybatis原始dao开发和mapper代理开发,整个项目数据库只用到了user表来演示如何封装的

    1.1先建立包装类

    package cn.eugene.po;
    
    public class UserQueryVo {
        private UserCustom userCustom;
    
        public UserCustom getUserCustom() {
            return userCustom;
        }
        public void setUserCustom(UserCustom userCustom) {
            this.userCustom = userCustom;
        }
        @Override
        public String toString() {
            return "UserQueryVo [userCustom=" + userCustom + "]";
        }   
           //下面可以封装商品类和订单类
    
    }
    
    

    包装类很简单,就是将普通类作为一个属性进行封装,这里使用的是UseCutom没有使用User,由于正式开发中可以采用自动化工具程序自动生成po类,一般不会去修改po类,我们可以使用一个类来拓展po类,这里我们使用UserCustom来代替User

    package cn.eugene.po;
    
    public class UserCustom extends User{
    
    }
    
    package cn.eugene.po;
    
    public class User {
        private int id;
        private String userName;
        private String userAge;
        private String userAddress;
        public User() {
            super();
            // TODO Auto-generated constructor stub
        }
        /**
         * @return the id
         */
        public int getId() {
            return id;
        }
        /**
         * @param id the id to set
         */
        public void setId(int id) {
            this.id = id;
        }
        /**
         * @return the userName
         */
        public String getUserName() {
            return userName;
        }
        /**
         * @param userName the userName to set
         */
        public void setUserName(String userName) {
            this.userName = userName;
        }
        /**
         * @return the userAge
         */
        public String getUserAge() {
            return userAge;
        }
        /**
         * @param userAge the userAge to set
         */
        public void setUserAge(String userAge) {
            this.userAge = userAge;
        }
        /**
         * @return the userAddress
         */
        public String getUserAddress() {
            return userAddress;
        }
        /**
         * @param userAddress the userAddress to set
         */
        public void setUserAddress(String userAddress) {
            this.userAddress = userAddress;
        }
        @Override
        public String toString() {
            return "User [id=" + id + ", userName=" + userName + ", userAge=" + userAge + ", userAddress=" + userAddress
                    + "]";
        }   
    }
    
    

    1.2 编写statement

      <!-- 用户信息综合查询 -->
         <select id="findUserList" parameterType="cn.eugene.po.UserQueryVo" resultType="cn.eugene.po.UserCustom">
            select * from user where userName like '%${userCustom.userName}%'
        </select>
    

    这里需要格外的注意,新手犯错误一般是在这个地方
    1.sql语句要写对
    2.设计到的变量要写对,比如这里是userCustom.userName ,userCutom是UserQueryVo的属性,userName是userCutom的属性,写了程序就会报错
    比如写成了UserCustom.userName,程序报错找不到UserCustom这个属性

    org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'UserCustom' in 'class cn.eugene.po.UserQueryVo'
    ### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'UserCustom' in 'class cn.eugene.po.UserQueryVo'
    .........
    

    写成userCustom.Name报错找不到Name这个属性

    org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'userCustom.name'. Cause: org.apache.ibatis.ognl.NoSuchPropertyException: cn.eugene.po.UserCustom.name
    ### Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'userCustom.name'. Cause: org.apache.ibatis.ognl.NoSuchPropertyException: cn.eugene.po.UserCustom.name
    ......
    

    1.3编写 mapper接口和测试类

    注意遵循使用mapper接口的规范

        public List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;
    

    编写测试类

         //用户综合信息查询
        @Test
        public void testFindUserList() throws Exception {       
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //mybatis自动生成接口实现类代理对象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);             
            UserQueryVo userQueryVo = new UserQueryVo();
            UserCustom userCustom = new UserCustom();
            userCustom.setUserName("运费");
            userQueryVo.setUserCustom(userCustom);
            List<UserCustom> list = userMapper.findUserList(userQueryVo);
            System.out.println(list);
        }
    

    这个例子中只封装了User类,也可以在UserQuery中封装其他综合查询的类如商品订单,从而实现综合查询

    输出映射

    2.1 输出简单类型

    需求:用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。

    mapper.xml

          <!-- 符合条件的用户数查询 -->
         <select id="findUserCount" parameterType="cn.eugene.po.UserQueryVo" resultType="int">
            select count(*) from user where userName like '%${userCustom.userName}%'
        </select>
    

    mapper.java

    public int findUserCount(UserQueryVo userQueryVo) throws Exception;
    

    测试代码

                    @Test
            public void testFindUserCount() throws Exception {
                
                SqlSession sqlSession = sqlSessionFactory.openSession();
                //mybatis自动生成接口实现类代理对象
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                
                
                UserQueryVo userQueryVo = new UserQueryVo();
                UserCustom userCustom = new UserCustom();
                userCustom.setUserName("运费");
                userQueryVo.setUserCustom(userCustom);
                int count  = userMapper.findUserCount(userQueryVo);
                System.out.println("count: "+count);
            }
    

    2.2 输出pojo和pojo列表

    不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。
    在mapper.java指定的方法返回值类型不一样:
    1、输出单个pojo对象,方法返回值是单个对象类型

    public  User findUserById(int id) throws Exception;
    

    2、输出pojo对象list,方法返回值是List<Pojo>

    public List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;
    

    2.3 resultmap

    a、 为什么需要使用

    • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功
    • 如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象
    • 只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象
      当查询出来的列名和pojo属性名不一致的时候,会创建pojo对象,但部分值会为null,因为这部分的值和sql查询出来的值对应不上,如果解决对应这个问题呢?
      mybatis框架为我们提供了resultmap

    mybatis中使用resultMap完成高级输出结果映射。

    b、resultMap使用方法

    如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

    1、定义resultMap

    2、使用resultMap作为statement的输出映射类型

    2.4 具体例子

    需求: 将下面sql使用resultmap完成映射,User类中属性名和查询列名不一致。

    select id id_,userName userName_  from user where id =#{id}
    

    a、定义resultmap

      <!-- type表示resultMap最终映射的Java对象,id用来标示resultMap -->
        <resultMap type="user" id="userResultMap">
            <!-- id,查询结果集中的唯一标识,column 表示查询出的列名,property表示对应pojo属性名-->
            <id column="id_" property="id"/>
            <!-- result,查询结果集中的普通列名 -->
            <result column="userName_" property="userName"/>
        </resultMap>
    

    b、使用resultMap作为statement的输出映射类型

        <!-- 根据id查询用户信息,resultMap指定定义的resultMap,如果定义的resultMap在其他的xml中,前面要加上naespace -->
        <select id="findUserById2" parameterType="int" resultMap="userResultMap">
            select id id_,userName userName_  from user where id = #{id}
        </select>
    

    c、mapper.java

    public User findUserById2(int id) throws Exception;
    

    d、测试代码

        @Test
        public void testFindUserById2() throws Exception {
            
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //mybatis自动生成接口实现类代理对象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = userMapper.findUserById2(1);
            System.out.println(user);
        }
    

    从例子中可以看出,resultMap起到映射或则说对应的作用,当sql语句查询出来的列名和pojo属性名不一致的时候,可以使用resultMap来对应,resultMap作为statement的输出映射,resultMap同时映射到对应的Java对象

    三 动态sql

    3.1 什么是动态sql?

    mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
    需求:用户信息综合查询列表和用户信息查询列表总数这两个statement的定义使用动态sql。

    对查询条件进行判断,如果输入参数不为空才进行查询条件拼接。
    这是我们上面输入映射进行综合查询用的一个sql的statement

      <!-- 用户信息综合查询 -->
         <select id="findUserList" parameterType="cn.eugene.po.UserQueryVo" resultType="cn.eugene.po.UserCustom">
            select * from user where userName like '%${userCustom.userName}%'
        </select>
    

    将这个statement改写下,变成下面这样,可以看到我们加入了<where>和<if>来动态判断userCustom.userName这个属性,输入userCustom.userName或则不输入都不会有影响,这就是动态sql

    <select id="findUserCount" parameterType="cn.eugene.po.UserQueryVo" resultType="int">
            select count(*) from user 
            <where>
            <if test="userCustom!=null">        
                <if test="userCustom.userName!=null and userCustom.userName!=''">
                and userName like '%${userCustom.userName}%'
                </if>
            </if>
            </where>
        </select>
    

    测试代码和输入映射中的相同就不在演示了

    3.2 sql片段

    当多个页面的查询都用到了用户综合信息查询的时候,我们需要考虑将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。
    方便程序员进行开发。

    如何使用sql片段?
    1.sql片段定义

    <!-- id sql片段的唯一标识 ,经验:一般是基于单表来定义sql片段,这样sql的重用性才高,sql片段中不包含where-->
        <sql id="query_user_where">
             <if test="userCustom!=null">       
                <if test="userCustom.userName!=null and userCustom.userName!=''">
                and userName like '%${userCustom.userName}%'
                </if>
            </if>
        </sql>
    

    2.引用(使用include)

      <!-- 符合条件的用户数查询 -->
         <select id="findUserCount" parameterType="cn.eugene.po.UserQueryVo" resultType="int">
            select count(*) from user 
            <!-- where可以自动去掉条件中的一个and -->
            <where>
            <!-- refid,sql片段的标识 -->
            <include refid="query_user_where"></include>
            </where>
        </select>
    

    3.3 foreach

    向sql传递数组或List,mybatis使用foreach解析

    需求:在用户查询列表和查询总数的statement中增加多个id输入查询。
    sql语句如下:

    两种方法:
    SELECT * FROM USER WHERE id=1 OR id=10 OR id=16

    SELECT * FROM USER WHERE id IN(1,10,16)

    具体实例
    以综合查询用户数为例,查询用户名为运费id等于1的用户数有多少
    在输入参数类型中添加List<Integer> ids传入多个id,以上面的用户综合查询为例
    我们这里需要向自定义的对象中封装更多参数

    package cn.eugene.po;
    
    import java.util.List;
    
    public class UserQueryVo {
        private List<Integer> ids;
        public List<Integer> getIds() {
            return ids;
        }
        public void setIds(List<Integer> ids) {
            this.ids = ids;
        }
            ....
    }
    

    修改mapper.xml,这里用到了sql片段

    <!-- id sql片段的唯一标识 ,经验:一般是基于单表来定义sql片段,这样sql的重用性才高,sql片段中不包含where-->
        <sql id="query_user_where">
             <if test="userCustom!=null">       
                <if test="userCustom.userName!=null and userCustom.userName!=''">
                and userName like '%${userCustom.userName}%'
                </if>
            </if>
        </sql>
        <sql id="query_user_id">
             <if test="ids!=null">      
               <foreach collection="ids" item="user_id" open="and ("  close=")" separator="or">
               <!-- 每个遍历需要拼接的串 -->
               id=#{user_id}
               </foreach>
            </if>
        </sql>
       <!-- 符合条件的用户数查询 -->
         <select id="findUserCount" parameterType="cn.eugene.po.UserQueryVo" resultType="int">
            select count(*) from user 
            <!-- where可以自动去掉条件中的一个and -->
            <where>
            <!-- refid,sql片段的标识 -->
            <include refid="query_user_where"></include>
            <include refid="query_user_id2"></include>
            </where>
        </select>
    

    测试代码

                    //满足条件的用户数查询
            @Test
            public void testFindUserCount() throws Exception {
                
                SqlSession sqlSession = sqlSessionFactory.openSession();
                //mybatis自动生成接口实现类代理对象
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);             
                UserQueryVo userQueryVo = new UserQueryVo();
                UserCustom userCustom = new UserCustom();
                userCustom.setUserName("运费");
                userQueryVo.setUserCustom(userCustom);
                List<Integer> ids = new ArrayList<Integer>();
                ids.add(1);
                userQueryVo.setIds(ids);
                int count  = userMapper.findUserCount(userQueryVo);
                System.out.println("count: "+count);
            }
    

    另外一个sql的实现,sql片段

        <sql id="query_user_id2">
             <if test="ids!=null">      
               <foreach collection="ids" item="user_id" open="and id in("  close=")" separator=",">
               <!-- 每个遍历需要拼接的串 -->
               #{user_id}
               </foreach>
            </if>
        </sql>
    

    如果对你有所帮助的话,点个赞吧

    相关文章

      网友评论

          本文标题:mybatis输入映射、输出映射、动态sql

          本文链接:https://www.haomeiwen.com/subject/nmkslqtx.html