简介
要想学MyBatis,我们首先要知道MyBatis是什么?
MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以对配置和原生Map使用简单的XML或注解,将接口和Java的POJOs(普通的Java对象)映射成数据库中的记录。
它最大的作用就是是我们不用写那么多的JDBC代码和结果处理。本文主要内容是MyBatis是如何用的,该如何配置它。想要更加系统的学习MyBatis,请看官方参考文档:http://www.mybatis.org/mybatis-3/zh/index.html;(中文的哟)
如何用MyBatis
要使用MyBatis,只需要mybatis-x.x.x.jar文件至于classpath中即可。下载地址:https://github.com/mybatis/mybatis-3/tree/mybatis-3.4.2。或者用maven:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
要想使用MyBatis来做映射,我们需要一个SqlSession实例,而SqlSession实例需要从SqlSessionFactory 中来获取,这是一个工厂Bean,你们懂得,在实际的应用中,这个工厂Bean的作用域最好是应用作用域,也就是在应用的运行期间只创建一个就可以了。而它生产的SqlSession不是线程安全的,所有SqlSession的作用域最好是请求或方法作用域,不可以被共享,每一次使用都创建一个。接着说SqlSessionFactory,这个工厂Bean需要由SqlSessionFactoryBuilder 来创建,SqlSessionFactoryBuilder 可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例。SqlSessionFactoryBuilder 一旦创建了SqlSessionFactory就没有用了,所以它的最佳作用于也是方法作用域。
下面是创建过程:
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class DoMyBatis {
public void test() throws IOException{
String resource = "resources/mybatis-config.xml";//这是MyBatis的配置文件、
InputStream inputStream = Resources.getResourceAsStream(resource);//注意这里使用的是org.apache.ibatis.io.Resources
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
//在这里就可以使用sqlSession来执行对数据库的增删改查咯,比如这里执行一行查询
Blog blog = (Blog)sqlSession.selectOne("Mapper文件中Mapper元素的命名空间属性值.方法的ID属性值", "要传入的参数");
}finally{
sqlSession.close();//最好把关闭sqlSession放在finally中
}
}
}
配置文件以及Mapper XML
首先是配置文件介绍,也就是上面代码中的mybatis-config.xml
<?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>
<!--
个人认为主要需要定义的就是
environments
mappers
其他有些是为了方便使用,有些是为特殊定制,全凭需求决定,尤其是定制,用的不多
-->
<!--定义属性,这些属性都是可外部配置且可动态替换的。其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值-->
<properties resource="config.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value"
value="true"/><!-- 启用默认值功能(从3.4.2开始,默认false) -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator"
value="?:"/><!-- 替换默认值功能的分隔符(默认为:) -->
</properties>
<!-- seetings 是MyBatis中极为重要的调整设置,会改变Mybatis的运行时行为,具体请参考官方文档设置 -->
<settings>
<setting name="cacheEnabled" value="true"/><!-- 开启缓存 -->
<setting name="lazyLoadingEnabled" value="false"/><!-- 关闭延迟加载 -->
</settings>
<!-- typeAliases(类型别名),是为Java类设置一个短的别名。用来减少类完全限定名的冗余 -->
<typeAliases>
<typeAlias type="domain.blog.Author" alias="Author"/>
<!-- Author可以用在任何使用domain.blog.Author的地方 -->
<package name="domain.blog"/>
<!-- 扫描包domain.blog,如果Bean有注解@Alias("author"),则其别名为注解值,否者使用Bean的首字母小写的非限定类名 -->
</typeAliases>
<!-- typeHandlers(类型处理器)添加你重写的或自己创建的类型处理器来处理不支持的或非标准的类型 -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler" />
</typeHandlers>
<!-- 如果不想要默认的对象工厂,可以自己实现接口来创建对象工厂,然后在这里定义 -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
<!-- 这里你可以通过配置插件来拦截MyBatis所允许的一些方法调用,插件实现请看官方文档 -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
<!-- 配置环境。可以配置多个 -->
<environments default="development"><!-- defaule属性指定默认的环境ID -->
<environment id="development">
<!-- 配置事务管理器,JDBC或者 MANAGED-->
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<!-- 配置数据源, UNPOOLED、POOLED、JNDI-->
<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>
<!-- 设置数据库生产厂商,具体用法请看官方文档 -->
<databaseIdProvider type="DB_VENDOR">
<property name="..." value="..."/>
</databaseIdProvider>
<!-- 定义SQL映射语句的位置 -->
<mappers>
<mapper resource="org/mybatis/builder/test.xml"/>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
</configuration>
下面是Mapper XML
<?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="这里写命名空间">
<!-- 本文件旨在说明映射文件中各元素的属性配置 -->
<select
id="selectPerson"
parameterType="int"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256"
statementType="PREPARED"
resultOrdered="false"
resultSetType="FORWARD_ONLY"
databaseId="DB_VENDOR"
resultSets="...">
</select>
<!--
id:在命名空间中唯一的表示符,可以被用来引用这条语句
parameterType:将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为MyBatis可以通过TypeHandle推断出具体传入语句的参数,默认值unset。
resultType:从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。resultType和resultMap只能二选一,不能同时使用
resultMap:外部resultMap的命名引用。resultType和resultMap只能二选一,不能同时使用。
flushCache:如果将其设置为true,任何时候只要语句被调用,都将会导致本地缓存和二级缓存都被清空,默认值好:false。
useCache:将其设置为true,将会导致本条语句的结果被二级缓存,默认值:对select元素为true。
timeout:这个设置是在抛出异常之前,驱动程序等待数据库请求结果的秒数。默认值为unset(依赖驱动).
fetchSize:这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为unset(依赖驱动)
statementType:STATEMENT、PREPARED 或CALLABLE 的一个。这会让MyBatis分别使用Statement,PreparedStatement 或CallableStatement,默认值:PREPARED。
resultOrdered:这个设置仅针对嵌套结果select语句适用:如果为true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主记过行的时候,就不会发生有对前面结果集的引用的情况。
这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSetType:FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
databaseId:如果设置了databaseIdProvider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultSets:这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。
-->
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys="true"
timeout="20">
</insert>
<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
</update>
<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
</delete>
<!--
keyProperty:(仅对insert和update有用)唯一标记一个属性,MyBatis会通过getGeneratedKeys 的返回值或者通过insert语句的selectKey子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
useGeneratedKeys:(仅对insert和update有用)这会令MyBatis使用JDBC的getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像MySQL和SQL Server这样的关系数据库管理系统的自动递增字段),默认值:false。
keyColumn:(仅对insert和update有用)通过生成的键值设置表中的列名,这个设置尽在某些数据库(像PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
-->
<!-- ResultMap_begin -->
<!-- 普通的ResultMap及其应用——begin -->
<resultMap type="com.someapp.model.User" id="userResultMap">
<id column="user_id" property="id"/>
<result column="user_name" property="username"/>
<result column="hashed_password" property="password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id,user_name,hasded_password
from some_table
where id = #{id}
</select>
<!-- 普通的ResultMap及其应用——end -->
<!-- 高级结果映射——begin -->
<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_emial,
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 type="Blog" id="detailedBlogResultMap">
<constructor><!-- 将结果传入构造器 -->
<idArg column="blog_id" javaType="int"/>
<arg column="author_username" javaType="String"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author"><!-- 将结果传入Blog内部的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"><!-- 将结果集合posts中,集合类型为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"><!-- 鉴别器(就像Java中的Switch语句) -->
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
</mapper>
还有一个mapper(你也看见了,这里主要是为了介绍mapper XML的各种元素及功能,实际生产中你可以定义无线多个)
<?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="这里写命名空间">
<!-- 如果数据库支持自动生成主键的字段,设置useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了-->
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
<!-- 如果你的数据库还支持多行插入,你也可以传入一个Authors数组或集合 -->
<insert id="insertAuthors" useGeneratedKeys="true" keyProperty="id">
insert into Author (username,password,email,bio) values
<foreach collection="list" item="item" separator=",">
(#{item.username},#{item.password},#{item.email},#{item.bio})
</foreach>
</insert>
<!-- 对于不支持自动生成类型的数据库或可能不支持自动生成主键JDBC驱动来说,MyBatis有另外一种方法来生成主键 -->
<!-- 下面是从Oracle数据库的序列AUTHOR_SEQ中取下一个值来作为主键 -->
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
SELECT AUTHOR_SEQ.NEXTVAL FROM DUAL
</selectKey>
insert into Author(id, username, password, email,bio, favourite_section)
values (#{id},#{username},#{password},#{email},#{bio},#{favouriteSection,jdbcType=VARCHAR})
</insert>
<!-- selectKey元素的属性值如下 -->
<!--
keyProperty:selectKey语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表
keyColumn:匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
reultType:结果的类型。MyBatis通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis允许任何简单类型用作主键的类型,包括字符串。
如果希望作用于多个生成的列,则可以使用一个包含期望属性的Object或一个Map。
order:这可以被设置为BEFORE或AFTER。如果设置为BEFORE,那么它会首先选择主键,设置keyProperty然后执行插入语句。如果设置为AFTER,
那么先执行插入语句,然后是selectKey元素-这和Oracle的数据库相似,在插入语句内部可能有嵌入索引调用。
statementType:与前面相同,MyBatis支持TATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。
-->
<!-- 这个元素可以被用来定义柯重阳的SQL代码段-->
<sql id="sometable">
${prefix}Table
</sql>
<sql id="someinclude">
from
<include refid="${include_target}"></include>
</sql>
<select id="select" resultType="map">
select
field1,field2,field3
<include refid="someinclude">
<property name="include_target" value="sometable"/>
<property name="prefix" value="some"/>
</include>
</select>
<!-- 动态SQL -->
<!-- if -->
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state='ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
<!-- choose相当于Java中的switch -->
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state='ACTIVE'
<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>
</select>
<!-- where -->
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<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>
</select>
<!-- 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>
<!-- 这个set等价于下面的trim元素,注意逗号 -->
<update id="updateAuthorIfNecessary">
update Author
<trim prefix="SET" suffixOverrides=",">
<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>
</trim>
where id=#{id}
</update>
</mapper>
其中:SqlSession有这么多方法:
完
此致
敬礼!
O(∩_∩)O哈哈~