1.总结原始dao开发问题
- dao接口实现类中存在大量的模板代码,将模板代码提取出来减少代码量
- 调用statement时将参数硬编码了
- 调用sqlsession方法时传入变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错。不利于开发。
2.使用mapper代理开发
注意:
namespace命名空间,作用就是对sql进行分类管理,理解sql隔离。使用mapper代理开发,namespace有特殊重要意义,namespace等于mapper接口地址,如UserMapper.java
步骤:
创建与接口同名的mapper.xml文件,作为接口的实现,注意namespace的值应为接口的全路径
在SqlMapConfig.xml文件中引用mapper.xml文件
在接口中建立对应的方法,方法名,参数,返回类型要对应。
调用接口实现相应功能
UserMapper userMapper=session.getMapper(UserMapper.class); userMapper.findById(1);
3.加载属性配置文件db.properties
方法:
- 在SqlMapConfig.xml中引入db.properties文件。使用标签。然后结合el表达式。
- 还可使用标签配置相关属性
- 最后还会读取paramterType传递的属性,它会覆盖以读取的同名属性
- 通过parameterType传递的属性具有较高的优先权,resource或url加载的属性次之。最低优先级是properties元素体内定义的属性。
4.setting默认支持别名
<typeAliases>
<pakeage name=""><!-- 批量定义别名,自动扫描包。别名是类名大写-->
</typeAliases>
5.typeHandlers类型处理器
mybaits
中通过typeHandlers
完成jdbc
类型和java
类型转换。
6.加载mapper
单个加载mapper.xml使用
使用了java接口类,并将同名的mapper.xml放置同一个报下
原始的加载方法
批量加载mapper.xml文档
批量扫描包下的所有mapper.xml文件
7.包装类思想
在开发中,不一定就是单纯的对一个实体类进行增删改查,例如完成用户信息的综合查询,又是需要传入和传出的条件比较的复杂,可能包含用户信息,关联表的其他信息等。根据这种需求,在mybaits中我们可以定义包装类的pojo,在包装类型的pojo中将复杂的查询条件写进去。
//在这个包装类,不仅包含了User的实体类,还可以包含其他的实体类,如将用户和订单联合起来组成的复杂联合查询
//这时UserQueryVo可以将这些查询条件分装起来
//在里面定义订单实体类,并生成set,get方法
//查询条件和查询结果封装到一个类中,从contral,service,dao再依次返回
public class UserQueryVo{
private User user;
public User getUser(){
return user;
}
public void setUser(User user){
this.user=user;
}
...
...
}
8.resultType
和resultMap
使用resultType
进行输出映射,只有查询出的列名与pojo
的属性名一致时才能成功
如果查询出的列名与属性名不一致可以通过resultMap
定义一个对应关系
resultMap
使用在一对多,多对多的级联关系上。
resultType:新定义一个POJO继承某一个POJO,具有SQL语句查询的全部字段,作为一个查询结果的容器。这时因为一对多或者多对多的关系,显示的重复数据较多。使用双重for循环嵌套
resultMap:在处理一对多或者多对多关系时,将信息存储在list中,有自动去重的功能。
9.动态SQL
实现动态SQL表达式是基于JSTL表达式实现的,它与包装类配合使得查询语句更加的灵活,减少了sql语句代码。常用的动态sql元素主要有
if
:
<where>
<if test="userCustom!=null">
<if test="userCustom.username!=null and userCustom.username!=''">
user.username like '%${userCustom.username}%'
</if>
<if test="userCustom.sex!=null and userCustom.sex!=''">
and user.sex like '%${userCustom.sex}%'
</if>
</if>
</where>
where标签相当于sql中的where,将其标签化,使得sql语句更加的灵活。与if语句结合组成动态sql
foreach
//foreach可以迭代集合。有几个重要的参数
//collection是想要遍历集合的名称
//item是给集合起的别名
//open是结合sql语句,迭代位置起始的拼接语句,与之对应的close是结束时的拼接语句。与sql语句紧密相关
//separator是迭代时查询之间的连接
<foreach collection="list" item="list" open="and(" separator="or" close=")" >
user.id = #{list}
</foreach>
sql片段:将一块具有特定功能的语句抽取出来,如同一个函数,在需要的时候调用。大大的提高了代码的复用性。也使得代码界面更加的合理。
10.数据库表一对一,一对多,多对多关系分析
在分析表格之前一定要明白需求,根据需求确定关系。不能简单地说关系是一对一还是一对多,必须有一定的限定条件
11.一对一关系映射
想要实现一对一关系的映射必须实现表与表之间的关联,要有实在的主键和外键关系。结果的获得可通过resultType或resultMap
方式获得。
通过
resultType
方法:定义一个类继承一个主表的pojo
,另外还需要添加上查询条件上多出来的属性名。使得获取的数据全部装载到类中通过
resultMap
方法:设置resultMap
方法的id值,然后引用。resultMap
中需要填写type(返回数据的POJO) id(别名)aassociation
(将两个POJO联系起来,需要在POJO中写get,set对象方法)然后将字段名与属性名对应即可。中的property中指定的是POJO中对象名的变量名,javaType是对象类型
<!-- 使用resultMap实现一对一关系查询 --> <select id="findOrdersUserByResult" resultMap="getOrderResult"> SELECT orders.*, user.username, user.sex, user.address FROM orders, user WHERE orders.user_id = user.id </select> <resultMap type="Orders" id="getOrderResult"> <id column="id" property="id"/> <result column="user_Id" property="userId"/> <result column="number" property="number"/> <result column="createtime" property="createtime"/> <result column="note" property="note"/> <association property="user" javaType="User"> <id column="user_id" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="address" property="address"/> <result column="birthday" property="birthday"/> </association> </resultMap>
12.一对多关系映射
需求:显示订单全部信息,用户的用户名,用户地址,订单明细的id共三张表。牵涉的一对一,一对多级联查询。
SELECT
orders.*, USER .username,
USER .sex,
USER .address,
orderdetail.id orderderdetail_id
FROM
orders,
USER,
orderdetail
WHERE
orders.user_id = USER .id
AND orderdetail.orders_id = orders.user_id
定义resultMap:
牵涉一对一是使用,牵涉一对多时使用。首先在中间表里面设置对象属性,和list<对象>属性,并生成get和set方法。用于保存从数据库中查询到的信息。中有extends属性,可以用来继承一对一的resultMap,写上id即可。在 property的值为pojo中定义的变量名,还有ofType指定的是单个list中的对象名称。
13.多对多关系映射
在mybaits中不同于hibernate的配置文件,之间配置两个set集合,或者把多对多拆解成两个一对多,对表进行管理。mybaits中映射多对多,无需配置文件。只需弄清楚表与表之间的级联关系(一对多,一对一,多对多)在POJO中声明对象或者List对象集合,并且生成get和set方法。在写sql语句时要清楚主键和外键,并将它们对应起来。
在写resultMap时可能会牵涉到一对多,一对一嵌套的情况。将查询出的字段(别名)与POJO属性名对应即可。查询出的多条记录可能被封装到同一个对象中。
14.延迟加载
当表与表之间建立有级联关系时,这时先查询单张表,然后根据需求去查另外一张表。这时可以使用延迟加载技术。由于数据库在查询多张表时速度较慢,所以采用延迟加载技术可以提高查询的效率。
案例:查询订单并且关联到查询用户信息。
在SqlMapConfig.xml文件中配置
<settings>
<!--打开延迟加载开关-->
<setting name="lazyLoadingEnabled" value="true"></setting>
<!--取消立即加载,改为延迟加载-->
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
代码
<!-- 使用延迟加载,查询订单并且关联到查询用户信息 -->
<select id="findOrdersUserLazyLoading" resultMap="getOrderMap">
select * from orders
</select>
<resultMap type="Orders" id="getOrderMap">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<association property="user" javaType="User" select="com.whz.mybatis.mapper.UserMapper.findById" column="user_id">
<id column="user_id" property="id"/>
</association>
</resultMap>
<select id="findById" parameterType="int" resultType="User">
select *
from user where id=#{id}
</select>
OrdersMapper ordersMapper=session.getMapper(OrdersMapper.class);
List<Orders> list=ordersMapper.findOrdersUserLazyLoading();
System.out.println(list);
for(Orders orders:list){
System.out.println(orders.getUser().getUsername());
}
分析:根据xml数据库会首先执行查询全部订单的操作,因为有延迟加载,后面的select语句不会执行。在测试时,由于需要用户表中的用户名数据,这时数据库会去执行查询用户表的操作。而且在迭代过程中如果用户名相同,还是不会去执行差村内用户表的操作。只有涉及级联操作时才会使用到级联,它提高了同时查询多张表时的效率。
15.一级缓存
mybaits的一级缓存是默认开启的,在平常操作中经常用到。它是SqlSession级别的,就是指当执行一个查询操作后,只要不执行commit(增加,删除,修改)这些操作。缓存不会被清除,在该SqlSession被销毁前,执行同一语句不会再向数据库发送信息。直接在缓存中得到结果。
16.二级缓存
mybaits的二级缓存默认是关闭的,开启的方式是在SqlMapConfig.xml中设置开启
<setting>
<setting name="cacheEnabled" value="ture"></setting>
</setting>
然后在相应的Mapper.xml的标签下使用标签。这两步就开启了二级缓存,在使用二级缓存时一定要在响应的POJO类中实现序列化接口。在一次service中二级缓存是生效的,与一级缓存相似的地方在于,当有session执行了对应对象的commit(增,删,改)操作后。二级缓存就会被清空。二级缓存中存在击中率的问题,即是在缓存中查找了几次成功。
17.二级缓存的一些其他问题
useCache用来禁止二级缓存,在一个方法中设置这个参数为false
<select useCache="false"> 这种情况适用于每次查询操作后都需要最新的数据,禁止二级缓存,直接在数据库中查找数据,防止脏读情况的发生
flushCache执行多方法之后刷新缓存
<select flushCache="true">
由于mybaits的缓存的存储方式为整块的存储,当执行刷新操作时会将全部缓存清空。
分布式缓存:当使用多台服务器同时工作时,我们产生的缓存数据可能要供多台机器使用。例如,QQ是否已经登录等。mybaits通过整合ehcahe实现分布式缓存。
集成ehcahe实现分布式缓存方法:
导JAR包 :ehcache-core.jar mybaits-ehcache.jar
在需要实现ehcache二级缓存的mapper,xml的namespace下设置
在classpath下编写ehcahe.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <diskStore path="F:\develop\ehcache"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> </ehcache>
mybaits二级缓存存在的问题
- 对于访问多的请求且用户对于查询结果实时性要求不高此时采用mybaits二级缓存技术降低数据库的访问量,提升了访问速度。实现方法,通过设置刷新时间间隔,由mybaits每隔一段时间自动清空缓存,根据数据变化频率设置刷新间隔flushInterval,如30分钟,60分钟
- mybaits二级缓存对于细粒度的数据级别的缓存实现不好,比如:对商品信息进行缓存,由于商品信息查询量大,但是要求每次查询最新的商品信息,此时使用mybaits的二级缓存就无法实现针对于一件商品信息的修改。它会将二级缓存全部清空,因为它以mapper为一个单位。
18.namespace命名规范
namespace命名是:所在包名+mapper.xml文件名(去掉xml后缀)
19.将spring与mapper接口进行整合
在applicationContext.xml文件中位置
<!-- 配置mapper接口扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.whz.mybatis.mapper"></property>
</bean>
即可完成mapper.java接口的扫描。因为mapper.xml与其绑定,所以在扫描接口的同时也会自动扫描mapper.xml。
20.ApplicationContext容器
ApplicationContext是spring中较高级的容器,与BeanFactory类似,它可以加载配置文件中所有的bean,将所有的bean集合起来,当有请求时分配bean。还可以实现从属性文本中将文件信息和事件传递给指定的监听器。ApplicationContext包含了BeanFactory所有功能,BeanFactory在轻量级应用中使用。
最常见的ApplicationContext接口实现:
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext
- WebXmlApplicationContext
21.逆向工程
- 创建一个逆向工程,一般是新建一个工程用以保存全部逆向工程产生的文件。
- 需要的jar包:mybaits.jar,mybatis-generator-core.jar,mysql-connector-java-bin.jar,ojdbc.jar。还需要generatorConfig-base.xml,generatorConfig-business.xml,generatorConfig.xml三个配置文件。其中需要修改的是generatorConfig.xml将里面的包名称改为需要复制的包名称(重点:一定要提前修改好,不然后面会报错),
- 最后执行GeneratorSqlmap.java文件即可生成mapper和po文件,直接复制即可使用。
- 因为逆向工程默认实现的是接口,所以在使用时直接用
sqlSession
获取接口类即可 - 除了基本的增删改查之外,还有类是hibernate的criteria(条件),甚至更加的灵活。
- 条件的查询在Example.java,它根据字段的属性生成了对应的条件语句。
- 逆向工程生成的crud方法都是对于单表操作的。