MyBatis--框架学习

学习地址:点击打开链接

一、概要

什么是MyBatis:Mybatis是ibatis的新版本,它是一种支持SQL、存储过程和高级映射的持久层框架。它几乎避免了所有数据库的JDBC连接管理代码、手工设置参数、获取结果集等代码、事务管理代码。它可以将XML配置的对象映射到数据库记录。

优点:简单易学、灵活(sql写在xml里,便于统一管理)、解除sql与程序代码的耦合、提供对象映射标签、提供对象关系映射标签、提供编写动态sql的xml标签。

缺点:编写sql语句的工作量大、移植性差(sql语句依赖于数据库)、二级缓存机制不佳(会有脏数据)。

mybatis项目的目录结构:

/my_application
  /bin
  /devlib
  /lib                <-- MyBatis *.jar文件在这里。
  /src
    /org/myapp/
      /action
      /data           <-- MyBatis配置文件在这里, 包括映射器类, XML配置, XML映射文件。
        /mybatis-config.xml
        /BlogMapper.java
        /BlogMapper.xml
      /model
      /service
      /view
    /properties       <-- 在你XML中配置的属性 文件在这里。
  /target
    /org/myapp/
      /action
      /data
      /model
      /service
      /view
    /properties
  /web
    /WEB-INF
      /web.xml
二、XML配置

*属性配置:读取顺序为:属性文件定义的属性->resource/url 属性指定的属性->参数传递的属性

*设置配置:用于设置mybatis的行为,如懒加载、缓存等。

*类全限定名的别名配置:用于简化类的全限定名冗余。

*类型处理器配置:用于处理sql执行后结果到对象的转换。有注解配置方法和

*对象工厂配置:用于创建sql处理结果对应的对象。

扫描二维码关注公众号,回复: 3742675 查看本文章

*插件配置:用于拦截Executors、ParameterHandler、ResultSetHandler和StatementHandler等的部分方法调用。具体地,实现Interceptor接口,指定想要拦截的方法签名,在intercept()方法中可以实现方法调用监控等功能,类似于AOP。

*环境配置:配置数据库连接管理的相关信息,包括事务管理器(JDBC、MANAGED)、数据源(选择POOLED、UNPOOLED、JNDI三种类型中的一种,定义数据库连接的信息)、databaseIdProvier(用于处理不同数据库)。

*映射器配置:支持映射文件的类相对路径配置、映射文件的绝对路径、接口的全限定名和注册包下的所有接口四种类型。

<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <package name="org.mybatis.builder"/>
</mappers>

三、XML映射

(1)<insert>|<update>|<delete>

可定义的属性有:id、parameterType、keyProperty、keyColumn、useCache、outTime、statementType、databaseId。

可用的子元素:<selectKey/>

(2)<select>

可定义的属性有:(1)中包含的、resultType、resultMap、fetchSize等。

(3)<sql>

sql标签用于定义可被重用的sql语句:

<sql id="mysql">${table.name},${table.age},${table.gener},${table.favorition}</sql>
<select id="selectUser">
    select
       <include refid="mysql"><property name="table" value="user"/></include>
    from user
</select>

(4)参数

使用#{}会使MyBatis创建预处理语句参数,并用?来标识该参数。

简单类型参数:int、short、float、boolean等:

<select id="selectUser">
    select * from user where id=#{id}
</select>

对象类型参数:

<insert id="insertUser" parameterType="User">
    insert into user(name,age,gender) values(#{name},#{age,jdbcType=int,javaType=int},#{gender,typeHandler=MyHandler})
</select>
<insert id="insertUser" parameterType="User">
    insert into tree(height,age) values(#{height,javaType=double,jdbcType=numeric,numericScale=2},#{age})
</select>

注意:需要为空类型指定jdbcType。

输出参数:用mode=OUT属性

<select id="selectDepartment">
    select #{department,mode=OUT,jdbcType=CURSOR, javaTypeResultSet, resultMap=departmentResultMap} from department where id=#{id}
</select>

结构体输类型参数:

<select id="selectDepartment">
    select #{department,mode=OUT,jdbcType=STRUCT, javaTypeResultSet, resultMap=departmentResultMap} from department where id=#{id}
</select>
不变字符串参数:存在sql注入的危险。
order by ${columnName}

(5)resultMap

基于Map的简单映射:

<select id="selectUsers" resultType="Map">
    select id,username,hashedPassword
    from manager
    where id=#{id}
</select>

基于JavaBean的简单映射:这种情况下,MyBatis会自动在后台建立一个resultMap

<!-- mybatis-config.xml -->
<typeAlias type="com.luoqing.model.Manager" alias="Manager">

<!-- SQL Mapping XML file-->
<select id="selectUsers" resultType="Manager">
    select 
        user_id as "id",                  <!-- 解决列名不匹配的一种方案 -->
        user_username as "username",
        hashed_password as "hashedPassword"
    from manager
    where id=#{id}
</select>
resultMap用于基于JavaBean的映射:
<resultMap id="managerResultMap" type="Manager">
    <id property="id" column="user_id"/>
    <result property="username" column="username"/>
    <result property="pswd" column="password/>
</resultMap>

<select id="selectUsers" resultMap="managerResultMap">
    select 
        user_id,username,pswd
    from manager
    where user_id=#{id}
</select>

resultMap用于高级映射:可以将一系列的关联查找结果映射到关联对象中取。例如,查找一篇博客,包括该博客的信息、博客的作者信息、博客的文章、文章的一系列评论、文章的作者、文章一系列标签、以及依据该博客的某个参数值决定输出字段。

<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.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 column="blog_id" javaType="int"/>
        <arg column... javaType.../>
    </constructor>
    <result column="blog_title" property="title" />
    <association property="author" column="blog_author_id" javaType="Author">//作者对象属性
        <id column="blog_title" property="id">
        <result column="author_username" property="username"/>
        <result column="author_password" property="password"/>
        <result column="author_email" property="email"/>
        <result column="author_bio" property="bio"/>
    </association>
    //或者
    <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>

    <collection property="posts" ofType="Post">//博文集合属性
        <id column="post_id" property="id"/>
        <result column="post_subject" property="subject"/>
        <collections property="comments" javaType="Comment">
            <id column="comment_id" property="id"/>
        </collections>
        <collection property="tags" ofType="Tag">
            <id column="draft_id" property="id"/>
        </collection>
    </collection>
    <discriminator column="draft" javaType="int">//依据查询结果设置对象属性值
        <case value="1" resultType="DraftPost"/>
    </discriminator>
 </resultMap>

关联association:前面的resultMap通过嵌套结果来进行对象的关联,其实还有两种更灵活的处理方式,即嵌套查询(如下例)和嵌套结果映射(如下下例):嵌套查询可能会导致N+1问题,懒加载在不需要立即使用查询的属性时可以解决该问题,将查询分散在不同时段,但如果要立即使用刚刚查询的属性,懒加载只会让性能变得更糟。

<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">
    <association column="author_id" property="author" select="selectAuthor"/>
</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
  CA.id           as co_author_id,
  CA.username     as co_author_username,
  CA.password     as co_author_password,
  CA.email        as co_author_email,
  CA.bio          as co_author_bio
from Blog B left outer join Author A on B.author_id = A.id
left outer join Author CA on B.co_author_id = CA.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"/>
    <association property="coAuthor" column="co_author_id" javaType="Author" resultMap="authorResult" columnPrefix="co_"/>//复用authorResult
</resultMap>
<resultMap id="authorResult" type="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="pswd" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="bio"/>
</resultMap> 

集合collection:和关联类似,除了结果嵌套外,还有查询嵌套和结果映射嵌套两种类型:

<resultMap id="blogResult" type="Blog">
  <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectPostsForBlog" resultType="Blog">
  SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>
<resultMap id="blogResult" type="Blog">
  <id property="id" column="blog_id" />
  <result property="title" column="blog_title"/>
  <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/>
</resultMap>

<resultMap id="blogPostResult" type="Post">
  <id property="id" column="id"/>
  <result property="subject" column="subject"/>
  <result property="body" column="body"/>
</resultMap>
鉴别器:
<resultMap>
    <discriminator javaType="int" column="vehicle_type">
        <case value="1" resultMap="carResult"/>
        <case value="2" resultMap="truckResult"/>
        <case value="3" resultMap="suvResult"/>
    </discriminator>
</resultMap>

自动映射:

    *通常数据库采用_ID_类型的大写、下划线命名,而java采用驼峰命名,为了让Mybatis实现自动映射,需要将mapUnderscoreToCamelCase设为true。

      *MyBatis自动映射不区分大小写。

     *MyBatis都是先自动映射,自动映射完了才进行手工映射。当resultMap中没有为属性配置手工映射,MyBatis会自动映射。

     *自动映射包含NONE、PARTIAL、FULL三种级别,默认值是PARTIAL。

缓存:

    在映射文件开头加上下列配置即开启了二级缓存。

<cache/>

    默认情况下,缓存具有如下特性:

  • 映射语句文件中的所有 select 语句将会被缓存。
  • 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

    也可以修改上述配置:

<cache
  eviction="FIFO"    //LRU、FIFO、SOFT、WEAK
  flushInterval="60000"
  size="512"
  readOnly="true"/>
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

四、动态SQL

动态SQL一般作用于WHERE子句中,用于条件查询。

动态SQL包括四种语句:<if>、<choose><when><otherwise>、<trim><where><set>、<foreach>。

使用示例:

//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 when otherwise
<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,where元素会依据if条件成立的个数动态添加where关键字,也会在当where子句中以AND、OR开头时去掉连接词
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG 
  <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>
</select>
//set,用于条件更新,set元素会依据if条件成立的个数动态决定是否添加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>
//trim,将prefixOverrides中给出的关键词替换为prefix中指定的关键词
<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>
//foreach,当把List或Array对象传给MyBatis时,MyBatis会将该对象封装在一个Map中,并以类的小写名称为键,以该对象为值。即List会以"list"为键,而ArrayList会以array为键。
<select id="selectPostIn" resultType="Post">
    SELECT * FROM post WHERE ID IN
    <foreach item="item" index="index" collection="list"
        open="(" seperator="," close=")">
        #{item}
    </foreach>
</select>  
//bind,该元素可以使用OGNL表达式语言从上下文中获取一个变量
<select id="selectBologsLike" resultType="Blog">
    <bind name="pattern" value="'%' + _parameter.getTitle() + '%'">
    select * from blog where title like #{pattern}
</select> 
//多数据库支持,通过_databaseId来判断当前的databaseIdProvider
<insert id="insert">
    <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId=='oracle'">
            select seq_user.nextval from dual
        </if>
        <if test="_databaseId='db2'">
            select nextval for seq_user from sysibm.sysdummy1
        </if>
    </selectKey>
    insert into user values(#{id},#{name})
</insert> 

五、Java API

SqlSessionFactoryBuilder->SqlSessionFactory->SqlSession->Mapper/Transaction

如果使用Spring+Mybatis,那么创建SqlSession的工作将交给Spring来做,而不需要我们手工创建。

1.SqlSessionFactoryBuilder

SqlSessionFactory build(InputStream inputStream)//采用默认数据库配置
SqlSessionFactory build(InputStream inputStream, String environment)//指定数据库相关信息
SqlSessionFactory build(InputStream inputStream, Properties properties)//采用默认数据库配置,读取属性文件
SqlSessionFactory build(InputStream inputStream, String env, Properties props)//指定数据库相关信息并读取属性文件
SqlSessionFactory build(Configuration config)

使用示例:使用默认environment配置创建SqlSessionFactory

String resource="org/mybatis/builder/mybatis-config.xml";
InputStream inputStream=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(inputStream);

使用示例:使用Configuration来创建SqlSessionFactory。Configuration包含所有mybatis-config.xml配置的配置方法。

DataSource dataSource = BaseDataTest.createBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);

2.SqlSessionFactory

SqlSession openSession()//开启事务(不自动提交)、从配置读取数据源、使用数据源默认的事务隔离级别、预处理语句不会被复用,也不会批量处理更新
SqlSession openSession(boolean autoCommit)//是否自动提交
SqlSession openSession(Connection connection)//给定数据源
SqlSession openSession(TransactionIsolationLevel level)//设置自动隔离级别
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)//ExecutorType.SIMPLE为每个语句执行创建新的预处理语句、ExecutorType.REUSE重用预处理语句、ExecutorType.BATCH批量执行
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

3.SqlSession

1)执行语句方法

T selectOne(String statement, Object parameter)//必须返回一个,返回多个或0个都会抛出异常
List selectList(String statement, Object parameter)
Map selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
//无参重载版本
T selectOne(String statement)
List selectList(String statement)
Map selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)
//高级版本
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)//限制返回行范围
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)//自定义结果集处理器
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)

2)事务控制方法

//只有在设置transactionManager为JDBC才有效,设置为MANAGED时,SqlSession没有权限管理事务了
//默认情况下,MyBatis在执行了插入、删除、修改操作会执行提交事务,我们不需要手动提交。如果你修改了数据库,但没有执行增删改操作,你可以传入true参数,强制MyBatis提交。
void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)

3)清理缓存

//SqlSession实例在执行update、commit、rollback和close时会默认清除本地缓存。如果需要显示清除,看调用下面的方法
void clearCache()

4)关闭SqlSession

//最好是将close()方法放到finally块中,以确保session关闭
void close()

5)获取配置

Configuration getConfiguration()

6)使用映射器

T getMapper(Class type)

下面展示了一个接口是如何映射到SqlSession对应的方法的:

//用SqlSession获取Mapper时,只需要将下面这个接口的class对象传递给getMapper方法
//这个接口不需要进行任何实现,接口方法只需要对应映射语句即可
public interface AuthorMapper{
    //Author selectOne("selectAuthor",5);
    Author selectAuthor(int id);
    //List selectList("selectAuthors");
    List selectAuthors();
    //Map selectMap("selectAuthors","id");
    @MapKey("id")
    Map selectAuthors();
    //void insert("insertAuthor",author);
    int insertAuthor(Author author);
    //void update("updateAuthor",author);
    int updateAuthor(Author author);
    //void delete("deleteAuthor",author);
    int deleteAuthor(Author author);
}

六、SQL构建器

使用SQL类来构建SQL映射示例:个人感觉就是每个Sql关键词对应一个方法。

private String selectPersonSql() {
    return new SQL() {{
        SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
        SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
        FROM("PERSON P");
        FROM("ACCOUNT A");
        INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
        INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
        WHERE("P.ID = A.ID");
        WHERE("P.FIRST_NAME like ?");
        OR();
        WHERE("P.LAST_NAME like ?");
        GROUP_BY("P.ID");
        HAVING("P.LAST_NAME like ?");
        OR();
        HAVING("P.FIRST_NAME like ?");
        ORDER_BY("P.ID");
        ORDER_BY("P.FULL_NAME");
    }}.toString();
}

public String deletePersonSql() {
   return new SQL() {{
      DELETE_FROM("PERSON");
      WHERE("ID = ${id}");
   }}.toString();
}
public String selectPersonLike(final String id, final String firstName, final String lastName) {
   return new SQL() {{
      SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
      FROM("PERSON P");
      if (id != null) {
        WHERE("P.ID like ${id}");
      }
      if (firstName != null) {
        WHERE("P.FIRST_NAME like ${firstName}");
      }
      if (lastName != null) {
        WHERE("P.LAST_NAME like ${lastName}");
      }
      ORDER_BY("P.LAST_NAME");
   }}.toString();
}
SQL类中的方法:
1.SELECT(String)、SELECT_DISTINCT(String)、FROM(String)、JOIN(String)、INNER_JOIN(String)、LEFT_OUTER_JOIN(String)、RIGHT_OUTER_JOIN(String)、WHERE(String)、OR()、AND()、GROUP_BY(String)、HAVING(String)、ORDER_BY(String)、

2.DELETE_FROM(String)、INSERT_INTO(String)、SET(String)、UPDATE(String)、VALUES(String, String)
















猜你喜欢

转载自blog.csdn.net/qq_30186661/article/details/80399282