mybatis基础
一、mybatis XML配置文件
<!--这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递--> <properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性,默认是关闭的,使用见下边password --> </properties> <!--下边username值会使用properties的配置的值,driver会使用config.properties配置的值--> <!--通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password:pwd}"/><!-- 如果属性 'password' 没有被配置,'password' 属性的值将为 'pwd' --> </dataSource> <!--常用配置项,下边列出的都是默认值(可省略)--> <settings> <setting name="cacheEnabled" value="true"/><!--是否打开缓存--> <setting name="lazyLoadingEnabled" value="false"/><!--是启启用懒加载,特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态--> <setting name="aggressiveLazyLoading" value="false"/><!--是否启用任意延迟属性的调用会使带有延迟加载属性的对象完整加载--> <setting name="multipleResultSetsEnabled" value="true"/><!--是否允许单一语句返回多结果集--> <setting name="useColumnLabel" value="true"/><!-- 使用列标签代替列名,注意:不同的驱动在这方面会有不同的表现--> <setting name="useGeneratedKeys" value="false"/><!--允许 JDBC 支持自动生成主键,需要驱动支持--> <setting name="autoMappingBehavior" value="PARTIAL"/><!--指定 MyBatis 应如何自动映射列到字段或属性。 NONE 取消自动映射;PARTIAL 只自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)--> <setting name="defaultExecutorType" value="SIMPLE"/><!--配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新--> <setting name="defaultStatementTimeout" value="25"/><!--超时时间,无默认值--> <setting name="safeRowBoundsEnabled" value="false"/><!--允许在嵌套语句中使用分页(RowBounds)--> <setting name="mapUnderscoreToCamelCase" value="false"/><!--开启自动驼峰命名规则(camel case)映射--> <setting name="localCacheScope" value="SESSION"/><!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据--> <setting name="jdbcTypeForNull" value="OTHER"/><!--没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型--> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/><!--指定哪个对象的方法触发一次延迟加载--> </settings> <!--别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余--> <!--常见的 Java 类型已内建的相应的类型别名、不区分大小写的--> <typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases> <typeAliases><!--也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean--> <package name="domain.blog"/><!--每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author--> </typeAliases> <!--映射器--> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/><!--使用相对于类路径的资源引用--> <mapper url="file:///var/mappers/BlogMapper.xml"/><!-- 使用完全限定资源定位符(URL) --> <mapper class="org.mybatis.builder.AuthorMapper"/><!-- 使用映射器接口实现类的完全限定类名 --> <package name="org.mybatis.builder"/><!-- 将包内的映射器接口实现全部注册为映射器 -->
</mappers>
二、XML映射文件
1、查询
<select id="selectPerson" parameterType="int" resultType="hashmap"><!--hashmap是java对象映射的别名,也可自定义对象--> SELECT * FROM PERSON WHERE ID = #{id} </select> <!--已有的默认java对象别名有_byte,_long,_short,_int,_integer,_double,_float,_boolean,string,byte,long,short,int,integer,double,float,boolean,date,decimal,bigdecimal,object,map,hashmap,list,arraylist,collection,iterator--> <!--select所有可配置的属性--> <select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
2、新增
<insert id="insertAuthor"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert> <!--insert所有可配置的属性--> <insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20">
3、更新
<update id="updateAuthor"> update Author set username = #{username}, password = #{password}, email = #{email}, bio = #{bio} where id = #{id} </update> <!--update所有可配置的属性--> <update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
4、删除
<delete id="deleteAuthor"> delete from Author where id = #{id} </delete> <!--delete所有可配置的属性--> <delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
5、结果映射
<!--结果映射相关--> <select id="selectUsers" resultType="com.someapp.model.User"><!--com.someapp.model.User是java中类名--> select id, username, hashedPassword from some_table where id = #{id} </select> <!--以上语句MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 SELECT 语句中对列使用别名(这是一个基本的 SQL 特性)来匹配标签--> <!--对于列名属性名不匹配时也可使用resultMap标签方式解决--> <resultMap id="userResultMap" type="com.someapp.model.User"><!--在select语句中使用resultMap="userResultMap"就可以了--> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap> 相关标签 constructor - 用于在实例化类时,注入结果到构造方法中 idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能 arg - 将被注入到构造方法的一个普通结果 id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能 result – 注入到字段或 JavaBean 属性的普通结果 association – 一个复杂类型的关联;许多结果将包装成这种类型 嵌套结果映射 – 关联本身可以是一个 resultMap 元素,或者从别处引用一个 collection – 一个复杂类型的集合 嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个 discriminator – 使用结果值来决定使用哪个 resultMap case – 基于某些值的结果映射 嵌套结果映射 – case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个 注:id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。唯一不同是,id 元素表示的结果将是对象的标识属性,这会在比较对象实例时用到。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。 <!--将结果通过实体的构造方法映射到实体--> <constructor> <idArg column="id" javaType="int" name="id" /> <arg column="age" javaType="_int" name="age" /><!--name属性是构造方法中参数的名字,如果不指定名字,constructor中内容的顺序必须和构造方法参数顺序一致--> <arg column="username" javaType="String" name="username" /> </constructor>
6、联表查询
<!--联表子查询(不推荐 )--> <resultMap id="blogResult" type="Blog"> <association property="author" column="author_id" javaType="Author" select="selectAuthor"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectAuthor" resultType="Author"> SELECT * FROM AUTHOR WHERE 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"/> </resultMap> <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> <select id="selectBlog" 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" 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"/> </association> </resultMap>
<resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" 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> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap> 对应sql <select id="selectBlogDetails" 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>
三、动态SQL语法
if,choose (when, otherwise),trim (where, set),foreach
<if test="title != null"> <!--title可以包含一些掩码或通配符--> AND title like #{title} </if>
<!--供了“title”就按“title”查找,提供了“author”就按“author”查找的情形,若两者都没有提供,就返回所有符合条件的--> <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND author_name like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose>
<!--where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除--> <!--where解决问题的场景另一个方案就是在条件语句前加一个1=1,就不会出现where后边没有条件的而报语法错误的情况了,用了wherer标签sql语句里就不需要写where关键字了--> <where> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </where>
<!--set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号--> <update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>
<!--遍历集合,当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值--> <select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
<!--bind类似于别名--> <select id="selectBlogsLike" resultType="Blog"> <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" /> SELECT * FROM BLOG WHERE title LIKE #{pattern} </select>
<insert id="create" parameterType="com.dmall.rtp.po.esc.test.TestResult"> insert into test_result <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null">id,</if> <if test="createrId != null">creater_id,</if> <if test="createdTime != null">created_time,</if> <if test="updaterId != null">updater_id,</if> <if test="updatedTime != null">updated_time,</if> <if test="yn != null">yn,</if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null">#{id,jdbcType=BIGINT}, </if> <if test="createrId != null">#{createrId,jdbcType=BIGINT}, </if> <if test="createdTime != null">#{createdTime,jdbcType=TIMESTAMP}, </if> <if test="updaterId != null">#{updaterId,jdbcType=BIGINT}, </if> <if test="updatedTime != null">#{updatedTime,jdbcType=TIMESTAMP}, </if> <if test="yn != null">#{yn,jdbcType=TINYINT}, </if> </trim> </insert>
也可在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素,例:
@Update({"<script>", "update Author", " <set>", " <if test='username != null'>username=#{username},</if>", " <if test='password != null'>password=#{password},</if>", " <if test='email != null'>email=#{email},</if>", " <if test='bio != null'>bio=#{bio}</if>", " </set>", "where id=#{id}", "</script>"}) void updateAuthorValues(Author author);
默认情况下,使用 #{} 格式的语法会导致 MyBatis 创建 PreparedStatement 参数占位符并安全地设置参数(就像使用 ? 一样),
但也可以使用${}在 SQL 语句中插入一个不转义的字符串
所能达到的效果如下:
@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
上边两个可以合并如下:
@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);
四、其他
1、缓存
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行<cache/>
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
自定义缓存
也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
2、日志记录
MyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具
在应用的类路径中创建一个名称为 log4j.properties 的文件,文件的具体内容如下:
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=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
springboot脚本架项目参考地址:mybatis-springboot