Mybatis 知识点整理,有些问题不是不会,也不是不懂,只是没有见过!
<?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="mapper.BlogMapper">
<!--单独的cache作用:
所有的映射文件里的select都会被缓存
所有的映射文件里的insert、update和delete语句执行都会被清空缓存
缓存使用最近最少使用算法LRU来回收
缓存不会被设定的时间所清空
每个缓存可以存储1024个列表或对象的引用(不管查询方法返回的是什么)
缓存将作为“读/写”缓存,意味着检索的对象不是共享里的且可以被调用者安全的修改,而不会被其他调用者或线程干扰
-->
<cache/>
<!-- eviction:缓存回收算法
LRU:最近最少使用:移出最近最长事件内都没有被使用的对象
FIFO:先进先出:移出最先进入缓存的对象
SOFI:软引用:基于垃圾回收机制和软引用规则来移除对象(空间内存不足时才回收对象)
WEAK:弱引用:基于垃圾回收机制和弱引用规则来移除对象(垃圾回收器扫描到才进行回收)
flushInterval:刷新时间间隔?
size:缓存对象的大小以及环境中的可用内存空间
readOnly:只读缓存将对所有调用者返回同一个实例。
因此这些对象都不能被修改,这可以极大的提高性能。
可写的缓存将通过序列化来返回一个缓存对象的拷贝。这会比较慢,但是比较安全。所以默认值都是false -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
<!-- 引用另外一个命名空间配置的缓存 -->
<cache-ref namespace=""/>
<!-- 使用自定义缓存==>如果想实现自定义缓存,必须实现org.mybatis.cache.Cache这个接口
可以通过自己的缓存实现来完全重写缓存行为,或者通过创建第三方缓存解决方案的适配器 -->
<cache type="something.MyBlogCache">
<!-- 要配置自己的缓存,简单的添加一个公共的JavaBeans属性到缓存实现中,然后通过cache元素设置属性进行传递 -->
<property name="cacheFile" value="/tmp/my-blog-cache.tmp"/>
</cache>
<!-- 描述如何从数据库结果集里面加载对象 -->
<resultMap type="Blog" id="blog"></resultMap>
<!-- 能够被其他语句重用的sql块 -->
<sql id="columns">
ID as id,
AUTHOR as author,
TITLE as title,
CONTENT as content,
CLICKTIMES as clicktimes
</sql>
<!-- 根据主键查询 -->
<select id="selectBlogById" parameterType="int" resultType="Blog">
select <include refid="columns"/> from Blog where id = #{id}
</select>
<!--parameterType:参数类型
resultType:返回值类型的具体类名
resultMap:返回值类型的结果集
flushCache:如果为true,每次该语句调用都会清空缓存
useCache:如果为true,则该语句的结果集将被缓存
timeout:请求超时时间
fetchSize:从数据库获得记录条数
statementType:STATEMENT/~PREPARED/CALLABLE
resultSetType:FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE -->
<select id="exampleSelect"
parameterType="参数类型"
resultType="Blog"
resultMap="Blog"
flushCache="true"
useCache="true"
timeout="1000"
fetchSize="256"
statementType="STATEMENT"
resultSetType="FORWARD_ONLY"></select>
<!-- useGeneratedKeys : 数据库自动生成主键
keyProperty : 主键字段 -->
<insert id="exampleInsert" useGeneratedKeys="true" keyProperty="id"></insert>
<insert id="add" parameterType="Blog">
insert into blog (id,author,title,content)
values (#{id} , #{author} , #{title} , #{content} , #{clicktimes})
</insert>
<!-- 自定义数据类型转换,并指定转换类
numericScale:指定小数位精度 -->
<insert id="add2" parameterType="Blog">
insert into blog (id,author,title,content)
values (
#{id , javaType=int , jdbcType=NUMERIC , typeHandler=ExampleTypeHandler } ,
#{author} ,
#{title} ,
#{content},
#{clicktimes,javaType=int,jdbcType=NUMERIC,numericScale=0})
</insert>
<!-- 这种申明结果集的方法可以解决列名不匹配的问题,和columns的效果相同 -->
<resultMap type="Blog" id="blogs">
<!-- 各属性解释
property:映射数据库列的字段或属性(对应javaBean)
column:数据库中的列名或者列标签的别名(对应数据库列)
javaType:完整的java类名或者列标签名
jdbcType:数据库中该列的字段类型(这个属性在insert、update或delete的时候针对允许空的列有用)
typeHandler:数据类型处理器
-->
<id column="ID" property="id" />
<result column="AUTHOR" property="author"/>
<result column="TITLE" property="title"/>
<result column="CONTENT" property="content"/>
<result column="CLICKTIMES" property="clicktimes"/>
</resultMap>
<!-- 一个多表关联的复合查询 -->
<select id="selectBlogDetails" parameterType="int" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
<!-- 将上一步的多表关联查询映射到一个智能的对象模型中 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<!-- 实例化的时候通过构造器将结果注入到类中 -->
<constructor>
<!-- idArg:将结果集标记为ID,以方便全局调用
arg:注入构造器的结果集
使用构造器将主表数据注入类中 -->
<idArg column="blog_id" javaType="int"/>
</constructor>
<!-- 注入一个字段或者javaBean属性的结果
主表数据单独用result申明 -->
<result property="title" column="blog_title"/>
<!-- 复杂类型联合:许多查询结果合成这个类型
使用blog_author_id外键关联author表中需要查询的字段 -->
<association property="author" column="blog_author_id" javaType=" Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<!-- 复杂类型集合
使用post_id外键关联post表中需要的字段 -->
<collection property="posts" ofType="Post">
<!-- post.id -->
<id property="id" column="post_id"/>
<!-- post.subject -->
<result property="subject" column="post_subject"/>
<!-- post与author表关联的字段 -->
<association property="author" column="post_author_id" javaType="Author"/>
<!-- comment_id作为外键与post关联,所以申明在post的关联对象中 -->
<collection property="comments" column="post_id" ofType=" Comment">
<id property="id" column="comment_id"/>
</collection>
<!-- tag_id 作为外键与post关联 -->
<collection property="tags" column="post_id" ofType=" Tag" >
<id property="id" column="tag_id"/>
</collection>
<!-- 使用一个结果值,以确定使用哪个resultMap -->
<discriminator javaType="int" column="draft">
<!-- 基于不同的结果映射 -->
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
<!-- 一对一关联配置 -->
<!-- 使用association和select属性做嵌套查询
使用这种配置方法会导致N+1选择问题的产生
执行单条sql语句获得一个表的记录
对获得的列表中的每条记录,在执行一次联合查询用以加载详细信息
这样会导致成千上万条sql执行,并非可取 -->
<resultMap type="Blog" id="blogResult">
<association property="author" column="blog_author_id"
javaType="Author" select="selectAuthor" />
</resultMap>
<select id="selectBlog" resultMap="blogResult" parameterType="int">
select * from blog where id = #{id}
</select>
<select id="selectAuthor" resultMap="author" parameterType="int">
select * from author where id = #{id}
</select>
<!-- 以上的结果相当于sql:
select b.*,a.* from blog b
left join author a on b.blog_author_id = a.id
where b.id = #{id} -->
<!-- 第二种映射结果集的方式 -->
<select id="selectBlog" parameterType="int" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio
from Blog B
left outer join Author A on B.author_id = A.id
where B.id = #{id}
</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column=" blog_id" />
<result property="title" column="blog_title"/>
<association property="author" column="blog_author_id" javaType="Author"
resultMap="authorResult"/>
<!-- lable209 -->
</resultMap>
<!-- 这样写便于查询结果集重用,如果只要用一次的话,直接将resultMap标签中的内容移动到lable209 -->
<resultMap id="authorResult" type="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</resultMap>
<!-- 一对多关联配置 -->
<resultMap id="blogResult" type="Blog">
<collection property="posts" javaType="ArrayList" column="blog_id"
ofType="Post" select="selectPostsForBlog"/>
</resultMap>
<select id="selectBlog" parameterType="int" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectPostsForBlog" parameterType="int" resultType="Author">
SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>
<!-- Discriminator:识别器~switch -->
<resultMap id="vehicleResult" type="Vehicle">
<id property="id" column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<discriminator javaType="int" column="vehicle_type">
<!-- 每当匹配中一个case,其余case将被忽略 -->
<case value="1" resultMap="carResult"/>
<case value="2" resultMap="truckResult"/>
<case value="3" resultMap="vanResult"/>
<case value="4" resultMap="suvResult"/>
</discriminator>
</resultMap>
<!-- 动态sql -->
<!-- if -->
<select id="findBlogTitleLike" parameterType="Blog">
select * from blog where 1=1
<if test="title != null">
and title like #{title}
</if>
</select>
<!-- choose -->
<select id="findBlogLike" parameterType="Blog">
select * from blog where 1=1
<choose>
<when test="title != null">
and title like #{title}
</when>
<when test="author != null and author.name != null">
and title like #{author.name}
</when>
<otherwise>
and featured = 1
</otherwise>
</choose>
</select>
<!-- where : 如果第一个条件不满足,但是第二个条件满足,则会忽略第二个条件的and -->
<select id="findBlogWithWhere" parameterType="Blog">
select * from blog
<where>
<if test="title != null">
title like #{title}
</if>
<if test="author != null and author.name != null">
and author like #{author.name}
</if>
</where>
</select>
<!-- trim==>where : 使用trim实现where同样的功能 -->
<select id="findBlogWithTrim" parameterType="Blog">
select * from blog
<trim prefix="where" prefixOverrides="and/or">
<if test="title != null">
title like #{title}
</if>
<if test="author != null and author.name != null">
and author like #{author.name}
</if>
</trim>
</select>
<!-- set -->
<update id="updateWithSet" parameterType="Blog">
update blog
<set>
<if test="author != null">author = #{author},</if>
<if test="content != null">content = #{content}</if>
</set>
where id = #{id}
</update>
<!-- trim==>set -->
<update id="updateWithTrim" parameterType="Blog">
update blog
<trim prefix="set" prefixOverrides=",">
<if test="author != null">author = #{author},</if>
<if test="content != null">content = #{content}</if>
</trim>
where id = #{id}
</update>
<!-- foreach -->
<!-- 注意:当传递一个集合给mybatis时,mybatis会自动将参数包装成map并且以名字作为key
list==》list,array==》array -->
<select id="selectBlogIn">
select * from blog
where id in
<foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
</mapper>
使用注解方式绑定sql
public interface BlogMapper { @Select("select * from Blog where id = #{id}") public Blog selectBlogById(int id); public void update(Blog blog); }
自定义的类型处理器
/** * @author Administrator * 自己的数据类型处理器 */ public class ExampleTypeHandler implements TypeHandler{ @Override public Object getResult(ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); } @Override public Object getResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getString(columnIndex); } @Override public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, (String) parameter); } }
自定义插件
@Intercepts( {@Signature(type = Executor.class, method = "update", args = {BlogMapper.class,Object.class})} ) public class ExamplePlugin implements Interceptor{ @Override public Object intercept(Invocation arg0) throws Throwable { return arg0.proceed(); } @Override public Object plugin(Object arg0) { return Plugin.wrap(arg0, this); } @Override public void setProperties(Properties arg0) { } }
自定义ObjectFactory
public class ExampleObjectFactory extends DefaultObjectFactory{ @Override public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } @Override public Object create(Class type) { return super.create(type); } /* 用来配置ExampleObjectFactory * 在初始化ExampleObjectFactory实例后,定义在ExampleObjectFactory元素主体中的属性会以参数的形式传递到此方法中 * @see org.apache.ibatis.reflection.factory.DefaultObjectFactory#setProperties(java.util.Properties) */ @Override public void setProperties(Properties properties) { super.setProperties(properties); } }
<?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> <!-- 引入配置文件 --> <properties resource="config/config.properties"> <!-- 如果config.properties文件中有username和password属性,再次配置将会覆盖文件中的值 --> <property name="username" value="bikeeverywhere"/> <property name="password" value="bikeeverywhere"/> </properties> <!-- 设置和改变Mybatis运行中的行为 --> <settings> <!-- 全局性的启用或者禁用所有在mapper配置文件中配置的缓存 --> <setting name="cacheEnable" value="true"/> <!-- 全局性的启用或禁用延迟加载。当禁用时,所有关联的配置都会立即加载 --> <setting name="lazyLoadingEnable" value="true"/> <!-- 当启用时,一个有延迟加载属性的对象的任何一个延迟属性被加载时,该对象的所有的属性都会被加载。 否则所有属性都是按需加载 --> <setting name="aggressiveLazyLoading" value="true"/> <!-- 允许或禁止从单一的语句返回多个结果集(需要驱动程序兼容) --> <setting name="multipleResultSetsEnable" value="true"/> <!-- 使用列的标签而不是列的名称 --> <setting name="userColumnLable" value="true"/> <!-- 允许JDBC自动生成主键(需要驱动程序兼容) --> <setting name="userGeneratedKeys" value="false"/> <!-- 指定mybatis是否以及如何自动将列映射到字段/属性 partial:自动映射简单的、非嵌套的结果集 full:自动映射任何复杂的(嵌套或非嵌套的结果集) --> <setting name="autoMappingBehavior" value="partial"/> <!-- 配置默认的执行器(executor) simple:简单的执行器 reuse:重用prepared statements的执行器 batch:重用statements并且进行批量更新的执行器 --> <setting name="defaultExecutorType" value="simple"/> <!-- 设置数据库查询超时时间 --> <setting name="defaultStatementTimeout" value="10000"/> </settings> <!-- 实体类设置别名 --> <typeAliases> <typeAlias type="entity.Author" alias="author"/> <typeAlias type="entity.Blog" alias="blog"/> <typeAlias type="entity.Comment" alias="comment"/> <typeAlias type="entity.Post" alias="post"/> <typeAlias type="entity.Section" alias="section"/> <typeAlias type="entity.Tag" alias="tag"/> </typeAliases> <!-- 数据库数据与java数据做类型转换使用 --> <typeHandlers> <!-- 可以重写类型处理器,或者建立自己的类型处理器去处理没有被支持的或非标准的类型 要做到这一点,只要实现TypeHandler接口(org.mybatis.type), 并且将TypeHandler类映射到java类型和可选的JDBC类型即可 --> <!-- 指定自己的数据类型转换,将VARCHAR转成String --> <typeHandler handler="handler.ExampleTypeHandler" javaType="String" jdbcType="VARCHAR"/> </typeHandlers> <!-- Mybatis每次创建一个结果对象都会使用objectFactory实例。 使用默认的objectFactory和使用默认的构造函数来实例化目标类并没有什么区别 --> <objectFactory type="factory.ExampleObjectFactory"> <property name="someProperty" value="100"/> </objectFactory> <!-- 自定义插件,在映射语句执行的某个时间点拦截方法调用 --> <plugins> <!-- 允许拦截的方法 --> <!-- Executor: query,update,flushStatements,commit,rollback,getTransaction,close,isClosed --> <!-- ParameterHandler: getParameterObject,setParameters --> <!-- ResultSetHandler: handlerResultSets,handlerOutputParameters --> <!-- StatementHandler: prepare,parameterize,batch,update,query --> <plugin interceptor="plugin.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins> <!-- 配置运行环境 可能出现一个sql语句向两个数据库发起请求,所以要配置多个数据源 但是:每个sqlsessionFactory只能选择一个运行环境 所以如果连接多个数据库,就要配置多个sqlsessionFactory--> <environments default="development"> <environment id="development"> <!-- 事物管理:JDBC/MANAGED --> <!-- JDBC:直接使用JDBC的提交和回滚功能,它依赖于从数据源获得连接来管理事物的生命周期 --> <transactionManager type="JDBC" /> <!-- 只有使用了延迟加载才需要数据源? --> <!-- Mybatis内置了3种数据源类型 --> <!-- UNPOOLED:每次需要的时候简单的打开和关闭连接 虽然有点慢,但是对于不需要立即响应的简单的应用来说,不失为一种好的选择--> <dataSource type="UNPOOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <environment id="product"> <!-- MANAGED:什么都不做,而是让容器来管理事务的生命周期 如果需要通过关闭连接来停止事务,将属性closeConnection设置为false--> <transactionManager type="MANAGED" > <property name="closeConnection" value="false"/> </transactionManager> <!-- POOLED:这个数据源的实现缓存了JDBC连接对象(池化技术) --> <dataSource type="POOLED"> <property name="driver" value="${driver1}"/> <property name="url" value="${url1}"/> <property name="username" value="${username1}"/> <property name="password" value="${password1}"/> <!-- 池化技术相关参数设定 --> <!-- 最大使用数量,默认为10 --> <property name="poolMaximumActiveConnections" value="10"/> <!-- 在特定时间内空闲的连接数(当没有人访问的时候也保持一定数量的连接) --> <property name="poolMaximumIdleConnections" value="5"/> <!-- 在连接池被强行返回前,一个连接能够“检出”的总时间,默认是20000ms --> <property name="poolMaximumCheckoutTime" value="20000"/> <!-- 这是一个比较底层的设置,如果连接占用了很长的时间,能够给连接池一个去打印日志的机会,并尝试重新连接,默认是20000ms --> <property name="poolTimeToWait" value="2000"/> <!-- ping query 是发送给数据库的ping信息,测试数据库是否良好和是否准备好了接受请求 默认值是 NO PING QUERY SET,让大部分数据库都不适用ping,返回一个友好的错误信息 --> <property name="poolPingQuery" value=""/> <!-- ping query 的开关,如果允许,需要设置一条可用的(最高效的)sql语句来设置pingquery的值 --> <property name="poolPingEnable" value="false"/> <!-- 这个属性设置ping query的间隔时间。通常设置为数据库连接的超时时间,避免不必要的ping 只有poolPingEnable=true时才生效 --> <property name="poolPingConnectionsNotUsedFor" value="0"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC" /> <dataSource type="JNDI"> <property name="driver" value="${driver2}"/> <property name="url" value="${url2}"/> <property name="username" value="${username2}"/> <property name="password" value="${password2}"/> <!-- 这个属性用来从InitalContext中查找一个上下文 --> <property name="initial_context" value=""/> <!-- 这个属性是引用一个能够被找到的数据源实例的上下文路径 它会查找根据 initial_context 从 initialContext 中搜寻返回的上下文。 或者在initial_context 没有提供的情况下直接在 InitialContext 中进行查找。 --> <property name="data_source" value=""/> </dataSource> </environment> </environments> <!-- 此处用于配置sql映射 --> <mappers> <!-- 引入根目录下的配置文件 --> <mapper resource="mapper/BlogMapper.xml"/> <!-- 使用资源路径引入 --> <mapper url="file:///var/sqlmaps/AuthorMapper.xml"/> </mappers> </configuration>