mybatis的一些特殊功能

 

 mybatis的一些特殊功能 special

    
    除了简化数据库编程外,MyBatis还提供了各种功能,这些对实现一些常用任务非常有用,比如按页加载表数据,存取CLOB/BLOB类型的数据,处理枚举类型值等等。
    4.1 处理枚举类型
        MyBatis支持持久化enum类型属性。假设t_user表中有一列gender(性别)类型为 varchar2(10),存储 MALE 或者 FEMALE 两种值。并且,Student对象有一个enum类型的gender 属性,如下所示:
        public enum Gender {  
            MALE,FEMALE  
        }  
        默认情况下MyBatis使用EnumTypeHandler来处理enum类型的Java属性,并且将其存储为 enum值的名称。你不需要为此做任何额外的配置。你可以像使用基本数据类型属性一样使用enum类型属性,如下:

表:

        drop table t_user;

        create table t_user(
          id number primary key,
          name varchar2(50),
          gender varchar2(10)
        );
pojo类:
        public class User{  
            private Integer id;  
            private String name;  
            private Gender gender;  
 
            //setters and getters  
        }            

SQLMapper.xml语句

        <insert id="insertUser" parameterType="User">  

            <selectKey keyProperty="id" resultType="int" order="BEFORE">
                select my_seq.nextval from dual
            </selectKey>
            insert into t_user(id,name,gender)  
            values(#{id},#{name},#{gender})  
        </insert>
        当你执行insertStudent语句的时候MyBatis会取Gender枚举(FEMALE/MALE)的名称,然后将其存储到GENDER列中。如果你希望存储原enum的顺序位置(0/1),而不是enum名,你需要明确地配置它
        如果你想存储FEMALE为0,MALE为1到gender列中,你需要在mybatis-config.xml文件中配置EnumOrdinalTypeHandler:  
        <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.briup.special.Gender"/>  
        注意:使用顺序位置为值存储到数据库时要当心。顺序值是根据enum中的声明顺序赋值的。如果你改变了Gender里面对象的声明顺序,则数据库存储的数据和此顺序值就不匹配了。       
    4.2 处理CLOB/BLOB类型数据
        BLOB和CLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的。通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。而像文章或者是较长的文字,就用CLOB存储.
        BLOB和CLOB在不同的数据库中对应的类型也不一样:
            MySQL 中:clob对应text/longtext,blob对应blob   
            Oracle中:clob对应clob,blob对应blob          
        MyBatis提供了内建的对CLOB/BLOB类型列的映射处理支持。       
        drop table user_pics;
        create table user_pics(id number primary key,name varchar2(50),pic blob,bio clob);  
        这里,照片可以是PNG,JPG或其他格式的。简介信息可以是学生或者讲师的漫长的人生经历。默认情况下,My Batis将CLOB类型的列映射到java.lang.String类型上、而把BLOB列映射到byte[]类型上。
pojo类:         
        public class UserPic{  
            private int id;  
            private String name;  
            private byte[] pic;  
            private String bio;  
            //setters & getters  
        }  
SQLMapper.xml语句         
        <insert id="insertUserPic" parameterType="UserPic">  
            <selectKey keyProperty="id" resultType="int" order="BEFORE">
                select my_seq.nextval from dual
            </selectKey>
            insert into user_pics(id,name, pic,bio)  
            values(#{id},#{name},#{pic},#{bio})  
        </insert>  
        <select id="getUserPicById" parameterType="int" resultType="UserPic">  
            select * from user_pics where id=#{id}  
        </select>  
 
        java代码:
        @Test
        public void test_insertUserPic(){  
            byte[] pic = null;  
            try {
                //读取用户头像图片
                File file = new File("src/com/briup/special/test.png");  
                InputStream is = new FileInputStream(file);  
                pic = new byte[is.available()];  
                is.read(pic);  
                is.close();  
            } catch (Exception e){  
                e.printStackTrace();  
            }  
            String name = "tom";  
            String bio = "可以是很长的字符串";
            //准备好要插入到数据库中的数据并封装成对象
            UserPic userPic = new UserPic(name, pic , bio);  
            SqlSession session = null;  
            try{  
                session = MyBatisSqlSessionFactory.openSession();
                SpecialMapper mapper = session.getMapper(SpecialMapper.class);
                mapper.insertUserPic(userPic);
                session.commit();  
            }catch (Exception e) {
                e.printStackTrace();
                session.rollback();
            }finally {
                if(session!=null)session.close();
            }
        }  
 
        下面的getUserPic()方法展示了怎样将CLOB类型数据读取到String类型,BLOB类型数据读取成byte[]属性:
 
        @Test
        public void test_getUserPicById(){
              SqlSession session = null;
            try {
                session = MyBatisSqlSessionFactory.openSession();               
                SpecialMapper mapper = session.getMapper(SpecialMapper.class);               
                UserPic userPic = mapper.getUserPicById(24);               
                System.out.println(userPic.getId());
                System.out.println(userPic.getName());
                System.out.println(userPic.getBio());
                System.out.println(userPic.getPic().length);
                
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if(session!=null)session.close();
            }
        } 
    4.3 传入多个输入参数
        MyBatis中的映射语句有一个parameterType属性来制定输入参数的类型。如果我们想给映射语句传入多个参数的话,我们可以将所有的输入参数放到HashMap中,将HashMap传递给映射语句。同时MyBatis还提供了另外一种传递多个输入参数给映射语句的方法。假设我们想通过给定的name和email信息查找学生信息,定义查询接口如下:
        对于映射器中的方法,MyBatis默认从左到右给方法的参数命名为param1、param2…,依次类推。
          
        public interface StudentMapper{  
            List<Student> findAllStudentsByNameEmail(String name, String email);  
        }  
        MyBatis支持将多个输入参数传递给映射语句,并以#{param}的语法形式引用它们:
        <select id="findAllStudentsByNameEmail" resultMap="StudentResult">  
            select stud_id, name,email, phone from Students  
            where  
            name=#{param1}  
            and  
            email=#{param2}  
        </select>  
        这里#{param1}引用第一个参数name,而#{param2}引用了第二个参数email。
        代码中调用:
        Student Mapper student Mapper = sql Session.get Mapper(StudentMapper.class);  
        student Mapper.findAllStudentsByNameEmail(name, email);  
    4.4 多行结果集映射成Map
        可以使用之前我们介绍到的接口的方式来实现(默认把列名作为key,列中的值作为value)。
        如果有一些特殊的情况,比如需要使用id值作为key,把一行数据封装成的对象作为value放到map中的话,需要使用下面的方式:       
        <select id="findAllUsers" resultType="User">  
            select id,name,gender from t_user  
        </select>
        Map<Integer, User> map = session.selectMap("com.briup.mappers.SpecialMapper.findAllUsers","id");
        for(Integer key:map.keySet()){
            System.out.println(key+" : "+map.get(key));
        }  
        注意:需要注意gender列的值都是数字还是都是字符串(需要一致)
        这里map将会将id作为key值,而每行数据封装成的User对象作为value值。
    4.5 使用RowBounds对结果集进行分页
        有时候,我们会需要跟海量的数据打交道,比如一个有数百万条数据级别的表。由于计算机内存的现实我们不可能一次性加载这么多数据,我们可以获取到数据的一部分。特别是在Web应用程序中,分页机制被用来以一页一页的形式展示海量的数据。
        MyBatis可以使用RowBounds逐页加载表数据。RowBounds对象可以使用offset和limit参数来构建。参数offset表示开始位置,而limit表示要取的记录的数目
        
        <select id="findAllUsers" resultType="User">  
            select id,name,gender from t_user  
        </select>  
        
        public List<User> findAllUsers(RowBounds rowBounds);
 
        然后,你可以加载第一页数据(前5条):
        int offset = 0;
        int limit = 5;  
        RowBounds rowBounds = new RowBounds(offset, limit);  
        List<Student> = studentMapper.getStudents(rowBounds);  
        若要展示第二页,使用offset=5,limit=5
        
        但是其实Mybatis的分页是基于内存的分页(查出所有记录再按偏移量和limit取结果),在大数据量的情况下这样的分页效率会很低。
        
        oracle使用rownum也可以完成分页:
        rownum 等于1
        rownum 大于0
        rownum 小于任何数
        例如:把sql语句查询结果当做一张表再查询
        select *
        from (
            select rownum as rowno, t.*
            from t_user t
            where rownum <= 10
        ) temp
        where temp.rowno >= 5;
     4.6 使用ResultHandler自定义结果集ResultSet处理
        MyBatis在将查询结果集映射到java对象方面提供了很大的选择性。但是,有时候我们会遇到由于特定的目的,需要我们自己处理SQL查询结果的情况。MyBatis提供了ResultHandler接口,可以让我们以任何自己喜欢的方式处理结果集ResultSet。
        例如:我们要把t_user表中所有数据的id和name查询出来,并且把id值作为key,把name值作为value封装到Map集合中
 
        注意:sqlSession.selectMap()则可以返回以给定列为key,记录对象为value的map。但是不能将其配置成使用其中一个属性作为key,而另外的属性作为 value。但是mybatis在之后的版本中可能会完成这个功能
        
 
        对于sqlSession.select()方法,我们可以传递给它一个ResultHandler接口的实现,它会被调用来处理ResultSet的每一条记录,而且完成我们上面的需求:
 
        @Test
        public void test_ResultHandler(){
            final Map<Integer,String> map = new HashMap<Integer, String>();  
            SqlSession session = null;
            try {
                session = MyBatisSqlSessionFactory.openSession();
                
                session.select("com.briup.mappers.SpecialMapper.findAllUsers", new ResultHandler<User>() {
 
                    @Override
                    public void handleResult(ResultContext<? extends User> resultContext) {
                        User user = resultContext.getResultObject();  
                        map.put(user.getId(), user.getName());  
                    }
                });
                
                for(Integer key:map.keySet()){
                    System.out.println(key+" : "+map.get(key));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if(session!=null)session.close();
            }
        }
 
        在上述的代码中,我们提供了匿名内部类对ResultHandler接口的实现,在handleResult()方法中,我们使用context.getResultObject()获取当前的result对象,即User对象,并对查询返回的每一行都会调用handleResult()方法,从而我们从User对象中取出id和name的值,将其放到map中。
 
 
    4.7 缓存
        将从数据库中加载的数据缓存到内存中,是很多应用程序为了提高性能而采取的一贯做法。默认情况下,mybatis会启用一级缓存;即,如果你使用同一个session对象调用了相同的SELECT语句,则直接会从缓存中返回结果,而不是再查询一次数据库。
        注意:session调用commit或close方法后,这个session中的一级缓存就会被清空  
        例如: 根据日志输出可以看出,下面代码只会发出一条sql查询语句
        @Test
        public void test_cache1(){
            SqlSession session = null;
            try {
                session = MyBatisSqlSessionFactory.openSession();
                
                SpecialMapper mapper = session.getMapper(SpecialMapper.class);
                
                User user1 = mapper.findUserById(21);
                System.out.println(user1);
                
    //            session.commit();
                
                User user2 = mapper.findUserById(21);
                System.out.println(user2);
                
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if(session!=null)session.close();
            }
        }
 
        二级缓存: 在不同的session对象之间可以共享缓存的数据
        1.mybatis-config.xml文件中保证<setting name="cacheEnabled" value="true"/>设置中是缓存功能是开启的,默认就是开启的true
        2.在需要二级缓存的xml映射文件中,手动开启缓存功能,在根元素中加入一个标签即可:<cache/>
        3.一个session查询完数据之后,需要调用commit或者close方法后,这个数据才会进入到二级缓存中,然后其他session就可以共享到这个缓存数据了
        
        注意:默认情况下,被二级缓存保存的对象需要实现序列化接口,可以通过cache标签的readOnly属性进行设置
 
        例如:
        mybatis-config.xml:
        <settings>
            <setting name="cacheEnabled" value="true"/>
        </settings>
 
        xml映射文件:
        <mapper namespace="com.briup.mappers.SpecialMapper">
            <cache/>
            <select> ..</select>
            <select> ..</select>
            <select> ..</select>
        </mapper>
 
        测试代码:
        @Test
        public void test_cache2(){
            SqlSession session1 = null;
            SqlSession session2 = null;
            try {
                session1 = MyBatisSqlSessionFactory.openSession();
                session2 = MyBatisSqlSessionFactory.openSession();
                
                SpecialMapper mapper1 = session1.getMapper(SpecialMapper.class);
                SpecialMapper mapper2 = session2.getMapper(SpecialMapper.class);
                
                User user1 = mapper1.findUserById(21);
                System.out.println(user1);
                session1.commit();
                
                User user2 = mapper2.findUserById(21);
                System.out.println(user2);
                session2.commit();
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if(session1!=null)session1.close();
                if(session2!=null)session2.close();
            }
        }
 
        二级缓存补充说明
          1. 映射语句文件中的所有select语句将会被缓存
          2. 映射语句文件中的所有insert,update和delete语句会刷新缓存
          3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
          4. 缓存会根据指定的时间间隔来刷新。
          5. 缓存会存储1024个对象
 
        cache标签常用属性:
        <cache  
        eviction="FIFO"  <!--回收策略为先进先出-->
        flushInterval="60000" <!--自动刷新时间60s-->
        size="512" <!--最多缓存512个引用对象-->
        readOnly="true"/> <!--true表示对象不能被写出去,即不可以被序列化,false表示可以写出去,即可以被序列化,默认值是false-->


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值