基础复习9-mybatis
配置:http://www.mybatis.org/mybatis-3/zh/configuration.html(上面写的很清楚)
Mybatis是一个轻量级ORM框架(持久层框架),前身为ibatis是apache软件基金会的一个开源项目,后来迁移到google code并更名为Mybatis。框架本身注重sql与pojo之间的映射关系
IDEA不会编译src下的java目录下的xml文件的,所以找不到xml文件,所以pom.xml里面需要配置
https://bglmmz.iteye.com/blog/2063856
http://www.cnblogs.com/tcming/p/7122124.html
<resources>
<resource>
<directory>src/main</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
一、Mybatis环境搭建
- 在项目中添加mybatis的支持库(mybatis-x.x.x.jar)
- 2.添加mybatis的核心配置文件(mybatis-config.xml)
层级结构:
A.属性配置
B.别名配置
C.环境配置
C1事务配置
C2.数据源配置
D.映射配置
3.(可选)添加jdbc.properties文件,提供数据库连接相关的字符串
4.添加实体类(DTO)、Mapper(DAO)接口以及Mapper接口的映射文件
二、Mybatis配置eclipse提示
- 解压mybatis.jar包
- 找到org/apache/ibatis/builder/mybatis-3-config.dtd
org/apache/ibatis/builder/mybatis-3-mapper.dtd
3.windows->preferences->xml->xml catalog->add->
三、Mybatis-config核心配置类的一些配置
TypeAliases:别名,在mapper.xml中也生效(parameterType、resultType)
<package name=”com.wan.pojo”> 默认为类名小字母手写
<typeAlias type="com.wan.pojo.User" alias="user"></typeAlias>
注解@Alias(“myuser”):(放在POJO类上)
优先于别名
Properties:配置属性资源路径
Properties里面还可以自己加<property>属性,jdbc.properties实际上也是加载成了属性
< Properties url=”file:/d:”/jdbc.properties”>存在于本地磁盘中(绝对路径)
< Properties url=”http://www.wan.com/jdbc.properties”>存在于本地磁盘中
< Properties resource=”jdbc.properties”>存在于项目中(相对路径)
Environment:环境配置,id是可以自己定义的,default指定一个id就可以了。
transactionManager:事务管理器
JDBC:默认关闭事务的自动提交,再手动提交
MANAGER:放弃事务管理,由其他事务管理器管理(如SPRING的管理器会覆盖前面的配置-AOP)
dataSource:数据源 使用连接池机制,
UNPOOLED:不使用连接池
POOLED:使用mybatis提供的默认连接池
JNDI:一般用于web项目中,主要通过在web容器(tomcart,jboos)中配置获取数据源
Mappers:映射配置
<mapper resource=”com/wan/mapper/UserMapper.xml”>:一次配置一个
<package name=”com.wan.mapper”>:一次配置多个
<mapper class=”com.wan.mapper.IUserMapper”>:直接配置在接口上,则没个方法上加注解写sql语句
<mapper url=”file:----”>:配置本地文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置属性资源路径(JDBC连接信息)-->
<properties resource="jdbc.properties"></properties>
<typeAliases>
</typeAliases>
<!--配置sqlSessionFactory的环境-->
<environments default="development">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源 使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--配置映射文件路径-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
属性文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=root
password=
-----------------------------------------------------------------
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.softeem.mapper.IUserMapper">
<!--
id保持与接口中方法名一致
-->
<!-- 查询操作 -->
<select id="selectAll" resultType="User">
select * from tbuser
</select>
<select id="findById" resultType="User">
select * from tbuser where id=#{uid}
</select>
<!-- 插入语句 -->
<insert id="insert" parameterType="User">
insert into tbuser(username,password) values(#{username},#{password})
</insert>
</mapper>
四、Mapper.xml映射文件配置
返回插入主键列为返回值(MySQL、SQLServer)(ORACLE自动递增不可以用这种方法)
#{XX} 如果参数类型是基本类型,写什么都无所谓,如果是引用类型则必须与属性名相同,
设置属性与列的映射关系
如果数据库列明与属性不同要配置,否则查找操作映射属性时相应属性取不到值,例如uid与表中id字段不一致,则uid取不到值。
用as设置列的别名也可以解决列明与属性名不一致的问题(从这里可以看出,是根据数据库列明进行反射得到属性的Getter方法进行属性赋值)
注:返回值int仍然是改变列的个数
user.getUid()才能得到主键列
五、多表关联查询
多对1或1对1:例如Users类里面Groups类属性
association:关联【column列一定要写,关联列】
1对多:Groups类里面有users = List<Users>属性
javaType:java集合类型
ofType:List中属性的类型
collection:
实现方法1:
<resultMap type="groups" id="groupUserInfo" autoMapping="true">
<id property="gid" column="gid"></id>
<collection property="userList" fetchType="eager" autoMapping="true" javaType="java.util.ArrayList" ofType="com.wan.pojo.User"
select="com.wan.mapper.IUserMapper.selectByGid">
</collection>
</resultMap>
实现方法2:
这种方法实际上是执行了两条sql语句
GroupMapper.xml:
<resultMap type="group" id=" groupInfo" autoMapping="true" > <association property="userList" fetchType="eager" column="gid" javaType="List" ofType=”user” autoMapping="true" select="com.wan.mapper.IUserMapper.selectByGid"></association>
</resultMap>
<select id="selectByPrimaryKey" resulMap="groupInfo "> select * from user where gid = #{gid}
</ select >
UserMapper.xml:
<select id="selectByGid" resulType="users "> select * from user where gid = #{gid}
</ select >
IUserMapper:
IGroupMapper:
Public Group selectByPrimaryKey(int gid);
六、动态sql
a、任意多个列组合模糊查询
- 可以用concat这些字段
- or语句(但如果列很多不适用)
- <if> <> <>语句
If中用and 和or连接条件
<bind>是声明变量的意思
Like用concat连接
<select id="selectByCondition" resultMap="userInfo">
select * from tbuser
where password = 123
<if test="username != '' and username != null">
<bind name="name" value="'%'+username+'%'"></bind>
/*or username like #{name}*/
or username like CONCAT('%',#{username},'%')
</if>
<if test="uid != '' and uid != null">
and id = #{uid}
</if>
</select>
b、多个条件只选其一,switch
按照when的顺序来匹配
<choose>
<when>
<when>
<choose>
c、多个set组合
d、多个insert into值
insert into tbemp(xxx) values(xxx);
e、多个delete from xxx where id in(xxxxx)
七、基于注解的mapper配置,摆脱xml文件的配置
例:查某groups,属性userList
<resultMap type="groups" id="groupUserInfo" autoMapping="true">
<id property="gid" column="gid"></id>
<collection property="userList" fetchType="eager" autoMapping="true" javaType="java.util.ArrayList" ofType="com.wan.pojo.User"
select="com.wan.mapper.IUserMapper.selectByGid">
</collection>
</resultMap>
八、延迟加载
如果是EAGER,那么表示取出这条数据时,它关联的数据也同时取出放入内存中
如果是LAZY,那么取出这条数据时,它关联的数据并不取出来,在同一个session中,什么时候要用,就什么时候取(再次访问数据库)。
但是,在session外,就不能再取了。用EAGER时,因为在内存里,所以在session外也可以取。
一般只在一边设Eager,JPA接口默认为一对多为Lazy,多对一为Eager,但是Hibernate反向工程生成Entity时,多对一为Lazy,需要手动改为Eager。
而两边都设Eager,那么代码中取一条记录时,会发2次SQL
在开发时,经常有这种需求,我拿到了一个order表单实体时,我需要知道该表单是属于哪个用户的。
这时访问 order.getUser() 获取用户信息,这时应该得到一个用户的信息实例。
解决这个问题的方法有三种:
1、通过1条SQL语句进行关联查询,同时读取表单和用户信息;
2、同时发送2条SQL,用于读取 order 与 user信息;
3、先用1条SQL读取order表单信息,访问getUser()时再用另1个SQL读取user信息;
第一种方法:使用关联查询的 mapper 的配置
1 <resultMap type="cn.xleos.mybatis.po.Order" id="orderUserMap">
2 <id property="id" column="orders_id" />
3 <result property="createtime" column="orders_createtime" />
4 <result property="number" column="orders_number" />
5 <result property="note" column="orders_note" />
6 <association property="user" javaType="cn.xleos.mybatis.po.User">
7 <id property="id" column="user_id" />
8 <result property="username" column="user_username" />
9 <result property="birthday" column="user_birthday" />
10 <result property="sex" column="user_sex" />
11 <result property="address" column="user_address" />
12 </association>
13 </resultMap>
14 <select id="getOrdersAndUser">
15 SELECT
16 orders.id AS orders_id,
17 orders.number AS orders_number,
18 orders.createtime AS orders_createtime,
19 orders.note AS orders_note,
20 `user`.id AS user_id,
21 `user`.username AS user_username,
22 `user`.birthday AS user_birthday,
23 `user`.sex AS user_sex,
24 `user`.address AS user_address
25 FROM orders
26 INNER JOIN `user` ON `user`.id = orders.user_id
27 </select>
第二种方法,使用二条SQL语句来完成读取表单与用户信息
在读取order关联时 association 节使用了 select="getUserById" 属性,它表示使用 getUserById 这个SQL来读取用户信息。
1 <resultMap type="cn.xleos.mybatis.po.Order" id="orderMap">
2 <id property="id" column="id" />
3 <result property="createtime" column="createtime" />
4 <result property="number" column="number" />
5 <result property="note" column="note" />
6 <association property="user" column="user_id"
7 javaType="cn.xleos.mybatis.po.User" select="getUserById" />
8 </resultMap>
9 <select id="getOrders" resultMap="orderMap">
10 SELECT id, user_id, number, createtime, note, FROM orders
11 <select>
12 <select id="getUserById" parameterMap="int" resultType="cn.xleos.mybatis.po.User">
13 SELECT id, username, birthday, sex, address FROM user where id = #{value}
14 <select>
通过日志可以发生在读取 order 数据时,就同时发送了2条SQL语句至数据库,读取 order 与 user 数据。
第三种方法,使用延时加载来访问 getUser() 时使用SQL语句读取user信息
1、需要在全局配置文件中添加lazyLoadingEnabled=true;
2、在关联映射(association)中设置 fetchType="lazy" 属性;
3、其它均与第二种方法一致;
1 <resultMap type="cn.xleos.mybatis.po.Order" id="orderMap">
2 <!-- 省略其它属性映射 -->
3 <association property="user" column="user_id"
4 javaType="cn.xleos.mybatis.po.User" select="getUserById" fetchType="lazy" />
5 </resultMap>
使用了延时加载后,在读取 order 表单时仅发送一个SQL语句,于数据库查询出 order 对象; 在访问 order.getUser() 时,再发送一条SQL至数据库,读取出 user 记录;
九、Mybatis代码生成器
十、mybatis日志_log4j
这里要把用到的类都加进去
log4j.logger.com.wan.mapper.IUserMapper=TRACE
log4j.logger.mapperTest.IUserMapperTest=TRACE
# Global logging configuration log4j.rootLogger=ERROR, stdout, FILE, log, INFO # MyBatis logging configuration... log4j.logger.com.wan.mapper.IUserMapper=TRACE log4j.logger.mapperTest.IUserMapperTest=TRACE # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n #FILE# log4j.appender.FILE=org.apache.log4j.FileAppender log4j.appender.FILE.Append=false log4j.appender.FILE.ImmediateFlush=true log4j.appender.FILE.File=log/log.log log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [ %t ] %m%n #LOG# log4j.appender.log=org.apache.log4j.DailyRollingFileAppender log4j.appender.log.FILE=log/log.log log4j.appender.log.Append=true log4j.appender.log.Threshold=INFO log4j.appender.log.DatePattern='.'yyyy-MM-dd log4j.appender.log.layout=org.apache.log4j.PatternLayout log4j.appender.log.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [ %t ] %m%n #INFO# log4j.appender.INFO=org.apache.log4j.DailyRollingFileAppender log4j.appender.INFO.FILE=log/log.log log4j.appender.INFO.Append=true log4j.appender.INFO.Threshold=INFO log4j.appender.INFO.DatePattern='.'yyyy-MM-dd log4j.appender.INFO.layout=org.apache.log4j.PatternLayout log4j.appender.INFO.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [ %t ] %m%n
十一、Mybatis缓存(Mybatis提供两个级别的缓存(查询缓存))
一级缓存:sqlSession级别:
在第一次查询时将从数据库获取的数据缓存到临时的内存空间中
以后每一次查询重复数据时不会再访问数据库,而是直接从缓存中获取,减少了对于数据库
IO操作,从而提高访问效率,该级别缓存默认开,如果有提交事务时,该级别缓存会被清空,
如果sqlSession被关闭,该级别缓存也会随之消失。session.commit()事务提交也会清除缓存
二级缓存:Mapper级别缓存:
该级别缓存不会因为sqlsession的关闭而清空,被缓存的数据依然存储在
Mapper级别,下一次重新获取sqlSession对象时还能使用该缓存区域的缓存数据;二级缓存需要(例如session.clearCache()清空缓存,再重新session = factory.openSession(); mapper = session.getMapper(UserMapper.class); 之前的缓存记录还在,针对于二级缓存)
配置:
1.在Mapper文件中配置<cache/>
2.缓存的对象所指代类必须实现序列化接口Serializable
<mapper namespace="com.softeem.mapper.UserMapper" >
<cache eviction="FIFO" flushInterval="60000"
size="512" readOnly="true"/>
eviction:回收策略(LUR,FIFO)
flushInterval:刷新间隔数(毫秒)
size:缓存的引用对象数目
readOnly:是否设置为只读缓存
十二、mybatis注意事项
servlet存在并发问题,所以不要设属性。
SessionFactoryBuilder用一次new一次,范围为方法内;
SessionFactory范围为单例,session每次用都重新new。
Service类:
Utils类: