zoukankan      html  css  js  c++  java
  • Mybatis动态sql及性能优化-3

    内容简介

    1.回顾
    2.动态sql
    3.性能优化
      懒加载机制
      一级缓存
      二级缓存
    

    一.回顾

    1.config文件常用标签
       properties标签:引入外部properties文件资源。
       settings标签:设置mybatis全局行为。
       typeAlias标签:减少mapper文件配置,给模型类起别名。
       transactionManager标签:配置mybatis的事务行为(JDBC|MANAGED)
       dataSource标签:配置mybatis数据源(POOLED|UNPOOLED|JNDI)
       mappers标签:引入Mapper文件或接口(url|resources|class|package)
    2.Mapper文件常用标签
      a. resultMap标签:对查询的结果进行封装处理
      				简单pojo类型处理,只需处理表中字段和对像中属性映射处理。
      				包装pojo类型处理,使用Collection处理包装集合类型的对像,association处理包含单个pojo对像。
      		Collection处理的两种方式:
            			1.连接查询join
            			2.通过多条select查询的方式。
            association处理两种方式:
            			1.连接查询join
            			2.通过多条select查询的方式。
       
       		discriminor鉴别器:根据查询结果中某个字段作为标识符,根据其查询的值,进行分类封装处理。
       	
       b.主键映射策略:如果主键由数据库生成,而不是手动指定,怎么获取该主键的问题。
       				一般使用selectKey标签处理。
      3.自定义封装结果集(了解):
      	按照自已定义方式把数据库返回结果封装成自己想要的类型。
      	一般自己建立结果的处理类,实现ResultHandler接口,重写里面的方法。
      	一般在select("statementId",params,ResultHandler实现类对像)
    

    二.动态Sql

    1.解决什么样问题

    用来解决sql语句where后条件的拼接问题,
    使用流程控制标签来完成条件的拼接:if标签,where标签,foreach标签。。。。
    

    2.常用动态sql标签

    1.if标签
    2.choose when otherwise标签
    3.trim标签
    4.foreache标签
    5.where标签
    6.set标签
    
    2.1 if标签
    和java中的if语句类似。
    
    	<select id="selectByItem" parameterType="User" resultType="User">
    		select * from USER 
    		where 1=1
            <!--mybatis如果是低版本时,如果是整形值,不要用‘’来判断;3.4.6版本没有问题-->
    		<if test="id != null and id !=''">
    			and id = #{id}
    		</if>
    		<if test="username !=null and username !=''">
    			and username=#{username}
    		</if>
    		<if test="password !=null and password !=''">
    			and password=#{password}
    		</if>
    		<if test="address !=null and address !=''">
    			and address=#{address}
    		</if>	 
    	</select>
    
    2.2 where标签
    根据查询条件是否存在,来决定是否生成where字符串。
    可以去除where后面紧跟的sql关键字, 如or或and
    
    <!-- where标签 -->
    	<select id="selectByItem2" parameterType="User" resultType="User">
    		select * from USER
    		<where>
    			<if test="id !='' and id != null">
    				and id = #{id}
    			</if>
    			<if test="username !=null and username !=''">
    				and username=#{username}
    			</if>
    			<if test="password !=null and password !=''">
    				and password=#{password}
    			</if>
    			<if test="address !=null and address !=''">
    				and address=#{address}
    			</if>
    		</where>	 
    	</select>
    
    2.3 choose when otherwise
    和java中switch case作用类似。
    不管有多少条件满足,只拼接其中一个条件。
    
    <!-- choose when otherwise -->
    	
    	<select id="selectByItem3" parameterType="User" resultType="User">
    		select * from USER
    		<where>
    			<choose>
    				<when test="id !='' and id != null">
    					and id = #{id}
    				</when>
    				<when test="username !=null and username !=''">
    					and username=#{username}
    				</when>
    				<when test="password !=null and password !=''">
    					and password=#{password}
    				</when>
    				<when test="address !=null and address !=''">
    					and address=#{address}
    				</when>
    				<otherwise>
    					and 1=1
    				</otherwise>
    			</choose>
    		</where>	 
    	</select>
    
    2.4 Set标签
    set用法和上面where用法一致。
    可以生成update语句中的set关键字,也可去除sql关键字,如逗号(set标签中sql字符串最后的逗号)
    
    <update id="update" parameterType="User">
    		update USER
    		<set>
    			<if test="username !=null and username !=''">
    				username = #{username},
    			</if>
    			<if test="password !=null and password !=''">
    				password=#{password},
    			</if>
    			<if test="address !=null and address !=''">
    				address=#{address},
    			</if>
    		</set>
    		where id = #{id}
    	</update>
    
    2.5 foreach标签
    类似于java中的foreach迭代。把传入sql语句中的数组类型或集合类型数据进行遍历操作。
    
    	<!-- 接口中参数为数组时,collection值必须为array -->
    	<select id="selectByIds" resultType="User">
    		select * from USER 
    		<foreach collection="array" item="id" open="where id in(" separator="," close=")">
    			#{id}
    		</foreach>
    	</select>
    
    	<!-- 接口中参数为pojo,collection值必须为该数组类型属性名 -->
    	<select id="selectByIds2" resultType="User" parameterType="User">
    		select * from USER 
    		<foreach collection="ids" item="id" open="where id in(" separator="," close=")">
    			#{id}
    		</foreach>
    	</select>
    
    2.6 trim标签
    可以删除sql语句指定的字符串。
    
    <!--
    	prefix="前缀":在后面sql字符串前面拼接指定的前缀。
    	prefixOverrides="要去除的字符":把紧跟着前缀后面的字符去掉。
    	suffix="后缀":在后面sql字符串后面拼接指定的后缀。
    	suffixOverrides="要去除的字符":把紧跟着后缀前面的字符去掉。
    -->
    
    <update id="update2" parameterType="User">
    		update USER
    			<trim prefix="set" prefixOverrides=",">
    				<if test="username !=null and username !=''">
    				,username = #{username}
    				</if>
    				<if test="password !=null and password !=''">
    				,password=#{password}
    				</if>
    				<if test="address !=null and address !=''">
    				,address=#{address}
    				</if>
    			</trim>
    		where id = #{id}
    	</update>
    	
    	<update id="update3" parameterType="User">
    		update USER
    			<trim prefix="set" suffixOverrides=",">
    				<if test="username !=null and username !=''">
    				username = #{username},
    				</if>
    				<if test="password !=null and password !=''">
    				password=#{password},
    				</if>
    				<if test="address !=null and address !=''">
    				address=#{address},
    				</if>
    			</trim>
    		where id = #{id}
    	</update>
    

    三.性能优化

    1.懒加载机制(lazy)

    当进行多表关联查询时,如果关联中数据暂时用不到,可以不去查询,当用到的时候,再发送sql语句去数据库关联出来。
    目的是:减少与数据库的交互行为,即减少数据库的开消。
    注意:
    join查询不支持lazy,多条select查询才支持lazy!
    

    2.步骤

    1.在setting标签中设置lazy开关
    	<settings>
    		<!-- 开启lazy加载 -->
    		<setting name="lazyLoadingEnabled" value="true"/>
    	</settings>
    2.使用select方式进行关联查询
    

    Model类

    //User类
    public class User {
    
    	private Integer id;
    	private String username;
    	private String password;
    	private String address;
    	
    	private List<Orders> list;
    }
        
    //Orders类
    public class Orders {
    	
    	private int id;
    	private String oname;
    	private int oprice;
        
    }
    

    UserMapper接口

    public interface UserMapper {	
    	public User selectById(User user);
    }
    

    UserMapper.xml

    <mapper namespace="lazy.mapper.UserMapper">
    
    	<resultMap type="Users" id="baseResultMap">
    		<id column="id" property="id"/>
    		<result column="username" property="username"/>
    		<result column="password" property="password"/>
    		<!-- 关联集合数据,其中ofType用来说明集合中的数据类型 -->
    		<collection property="list" ofType="Orders" select="lazy.mapper.OrdersMapper.selectByUid" column="id"></collection>
    	</resultMap>
    	
    	<select id="selectById"  resultMap="baseResultMap">
    		select * from USER where id = #{id}
    	</select>
    	
    </mapper>
    

    OrdersMapper接口

    public interface OrdersMapper {
    	public List<Orders> selectByUid(int uid);
    }
    
    

    OrdersMapper.xml

    <mapper namespace="lazy.mapper.OrdersMapper">
    
    	<select id="selectByUid" resultType="Orders">
    			select * from orders where uid = #{uid}
    	</select>
    
    </mapper>
    

    UserService类

    public class UserService {
    
    	public User findById(User user) {
    
    		SqlSession session = MybatisUtil.findSqlSession();
    
    		UserMapper mapper = session.getMapper(UserMapper.class);
    
    		User findUser = mapper.selectById(user);
    
    		session.close();
    
    		return findUser;
    
    	}
    
    }
    

    Test类

    public class TestLazy {
    
    	UserService ser = new UserService();
    	
    	@Test
    	public void testLazy(){
    		
    		User user = new User();
    		user.setId(1);
    		User findUser = ser.findById(user);
    		//当只使用User表中数据时,只发送一条查询User表的sql语句。
    		System.out.println(findUser.getUsername());
    		//当主动使用关联表中数据时,mybaits才再次发送查询语句去查询关联表。如果不使用,则该条sql将不会发送给数据库。
    		//System.out.println(findUser.getList());
    	}
    }
    

    3.一级缓存

    3.1缓存概念
    目的,减少与数据库的交互行为,当读取同一条数据时,优先从内存中的缓存中读取,如果能读到数据(命中数据),则就不需要交互数据库,如果读不到,则会交互数据库,查寻内容,放在缓存中,以备下一次查询时,在缓存中能命中数据,提高程序响应速度。
    
    3.2 mybatis的一级缓存
    1.一级缓存是默认使用的。
    2.一级缓存指的就是sqlsession范围内缓存,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区
    域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果
    对象。
    3.当数据库对应表发生增删改时,并执行commit操作时,默认会刷新一级缓存。
    如下图所示:
    

    第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信
    息。得到用户信息,将用户信息存储到一级缓存中。
    如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为
    了让缓存中存储的是最新的信息,避免脏读。
    第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户
    信息
    
    3.3缓存测试

    UserMapper.xml

    <mapper namespace="cache1.mapper.UserMapper">
    	<select id="selectById"  resultType="cache1.model.User">
    		select * from USER where id = #{id}
    	</select>
    </mapper>
    

    UserMapper接口

    public interface UserMapper {
    	public User selectById(User user);
    }
    
    

    UserService类

    public class UserService {
    
    	public User findById(User user) {
    
    		SqlSession session = MybatisUtil.findSqlSession();
    
    		UserMapper mapper = session.getMapper(UserMapper.class);
    
    		//查询两次
    		User findUser = mapper.selectById(user);
    		System.out.println(findUser.getUsername());
    		
            //如果不执行commit(),则发现执行两次查询时,只发送一条sql到数据库,则验证一级缓存存在。
    		//当执行commit()操作时,mybatis上下文会默认你进行增删改数据了,则会清空一级缓存;
    		session.commit(true);
    		
    		User findUser2 = mapper.selectById(user);
    		
    		System.out.println(findUser+"===="+findUser2.getUsername());
    
    		session.close();
    
    		return null;
    
    	}
    
    }
    
  • 相关阅读:
    (13)使用Ajax Helper 提高用户体验
    (12)改变图片的大小生成缩略图
    (11)通过表单上传文件
    程序员需要有多懒 ?- cocos2d-x 数学函数、常用宏粗整理
    xCode 4.X 免证书真机发布及调试
    35岁前必须做好的10件事情(转载)
    独自收集Cocos2d提供的字体!共57种(有对照的字体图)
    (10)根据关键字搜索
    tcp拥塞控制
    dpcnv reademe
  • 原文地址:https://www.cnblogs.com/zongJianKun/p/10328942.html
Copyright © 2011-2022 走看看