一、MyBatis 实际使用案例
编程式使用
JavaApi编程的方式使用。
1.引入Mybatis jar 包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4-snapshot</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
2.创建一个全局配置文件,里面是MyBatis一些核心行为控制,比如mybatis-config.xml。这里只定义的数据源和Mapper映射器路径。
<?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="db.properties"></properties>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!-- 控制全局缓存(二级缓存),默认 true-->
<setting name="cacheEnabled" value="true"/>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<!--<setting name="proxyFactory" value="CGLIB" />-->
<!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
<!--
<setting name="localCacheScope" value="STATEMENT"/>
-->
<setting name="localCacheScope" value="SESSION"/>
</settings>
<typeAliases>
<typeAlias alias="blog" type="com.dia.domain.Blog" />
</typeAliases>
<!-- <typeHandlers>
<typeHandler handler="com.dia.type.MyTypeHandler"></typeHandler>
</typeHandlers>-->
<!-- 对象工厂 -->
<!-- <objectFactory type="com.dia.objectfactory.GPObjectFactory">
<property name="gupao" value="666"/>
</objectFactory>-->
<!-- <plugins>
<plugin interceptor="com.dia.interceptor.SQLInterceptor">
<property name="gupao" value="betterme" />
</plugin>
<plugin interceptor="com.dia.interceptor.MyPageInterceptor">
</plugin>
</plugins>-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="BlogMapper.xml"/>
<mapper resource="BlogMapperExt.xml"/>
</mappers>
</configuration>
properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=123456
3.编写映射文件:Mapper.xml,通常来说对应一张业务表,我们会在这个mapper文件里编写增删改查的SQL语句,以及参数和结果集。
<?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.dia.mapper.BlogMapper">
<resultMap id="BaseResultMap" type="blog">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<!--
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.dia.type.MyTypeHandler"/>
-->
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="author_id" property="authorId" jdbcType="INTEGER"/>
</resultMap>
<select id="selectBlogById" resultMap="BaseResultMap" statementType="PREPARED" >
select * from blog where bid = #{bid}
</select>
</mapper>
4.使用Mybatis执行一个查询。
mybati的目的就是简化JDBC的操作,那么它必须要提供一个可执行增删改查的对象,在MyBatis里是SqlSession接口,他是和数据库建立的一个连接/会话。
问题来了,SqlSession是如何创建的?Mybatis核心行为的控制(各种配置项以及开启缓存等)都在全局的配置文件中,所以必须基于全局配置文件创建,mybatis 提供了一个工厂类来创建SqlSession。
public interface BlogMapper {
/**
* 根据主键查询文章
* @param bid
* @return
*/
public Blog selectBlogById(Integer bid);
}
public class BlogExampleTest {
@Test
public void TestExample() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
Blog blog = (Blog)session.selectOne("com.dia.mapper.BlogMapper.selectBlogById",new Integer(1));
System.out.println(blog.getBid()+">>>>"+blog.getName()+">>>>>"+blog.getAuthorId());
} finally {
session.close();
}
}
}
=====================================
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@223191a6]
==> Preparing: select * from `blog` where bid = ?
==> Parameters: 1(Integer)
<== Columns: bid, name, author_id
<== Row: 1, RabbitMQ延时消息, 1001
<== Total: 1
这样的调用方式,解决了重复代码、资源管理、SQL耦合、结果集映射的四大问题。
还存在问题:
1、Statement是硬编码,维护起来不方便;
2、不能在编译时候进行类型检查,如果namespace或者StatementID出错,只能在运行时报错。
mybatis给我们推荐了一种新的方法:定义一个Mapper接口的方式。这个接口全路径必须跟Mapper.xml里面的namespace对应起来,方法也要跟StatementID一一对应。
@Test
public void TestExample() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
// Blog blog = (Blog)session.selectOne("com.dia.mapper.BlogMapper.selectBlogById",new Integer(1));
// System.out.println(blog.getBid()+">>>>"+blog.getName()+">>>>>"+blog.getAuthorId());
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
} finally {
session.close();
}
}
============================================
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3023df74]
==> Preparing: select * from `blog` where bid = ?
==> Parameters: 1(Integer)
<== Columns: bid, name, author_id
<== Row: 1, RabbitMQ延时消息, 1001
<== Total: 1
二、MyBatsi的核心对象及其生命周期
SqlSessinFactoryBuilder
用来构建SqlSessionFactory的,然而SqlSessionFactory单例即可,完成构建SqlSessionFactory也就没有存在的意义了,生命周期局限于方法内部。
SqlSessionFactory(单例)
SqlSessionFactory用于构建SqlSession,应用程序每次访问数据库都需要创建一个会话。我们一直需要会话的存在,所以SqlSessionFactory的生命周期也是我们整个程序的生命周期(作用域是应用作用域)。创建SqlSession只需要一个人来做就行了,否则会产生混乱和浪费资源。所以采用单例模式。
SqlSession
SqlSession是一个会话,因为它不是线程安全的,不能在线程间共享。所以我们要在请求开始的时候创建一个SqlSession,在请求结束或者说方法执行完毕之后要及时关闭它(一次请求或者操作中)。
Mapper
其实是一个被代理对象,在SqlSession中获取
BlogMapper mapper = session.getMapper(BlogMapper.class);
他的作用是发送SQL来操作数据库的数据,它应该在一个SqlSession事务方法之内。
mybatis-核心对象生命周期三、核心配置解读
Configuration
配置类Configuration。它贯穿MyBatis执行流程的每个环节。这个类下面还有很多属性,跟其他的子标签也能对应上。
properties
第一个一级标签properties,用来配置参数信息,比如最常见的数据库信息。
为了避免直接把参数写死在xml文件中,我们可以单独吧一些数据信息单独放在properties文件中,用properties标签引进来,然后在xml文件中使用${}引入即可。
可以使用resource引用应用里面的相对路径,也可以使用url指定本地服务器或者网络的绝对路径。
setting
mybatis的一些核心配置
image.png image.png image.png image.png
typeAliases
typeAliases是类型的别名,跟linux系统的别名是一样的。可以为Bean创建别名,也可以指定单个类,或者指定一个package。
用法如下:
<typeAliases>
<typeAlias alias="blog" type="com.dia.domain.Blog" />
</typeAliases>
配置了别名在mapper就可以使用其别名了:
<resultMap id="BaseResultMap" type="blog">
<id column="bid" property="bid" />
<!--
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.dia.type.MyTypeHandler"/>
-->
<result column="name" property="name" />
<result column="author_id" property="authorId" />
</resultMap>
typeHandler
由于Java类型和数据库类型并不是一一对应的,比如(String与Varchar、char、text),所以我们要把对象转换为数据库对象的值,同理也需要把数据库对象的值转换为Java对象,这两个方向的转换,需要用到typeHandler。
当参数类型和返回值是一个对象时,我们没有做任何配置,为什么对象里面一个String属性,可以转换为数据库里面的VARCHAR?
这是因为Mybatis已经内置了很多TypeHandler(在type包下),他们全部都注册在TypeHandlerRegistry中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java数据类型。
这个也是为什么大部分类型都不需要处理。当我们查询数据和登记数据,做数据的类型转换时,就会自动调用对应Typehanlder方法。
objectFactory
当我们把数据库返回的结果集转换为实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类是什么,有哪些属性和类型。我们不能直接new Object(),只能通过反射来实现,objectFactory就是用来创建对象的工厂。
public interface ObjectFactory {
// 设置参数的时候调用
default void setProperties(Properties properties) {
// NOP
}
// 创建对象(无参数构造函数)
<T> T create(Class<T> type);
// 创建对象(有参数构造函数)
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
// 判断是否集合
<T> boolean isCollection(Class<T> type);
}
ObjectFactory有一个默认的实现类DefaultObjectFactory。创建对象的方法最终都调用了instantiateClass(),这里能看到反射的代码。
默认情况,所有的对象都由DefaultObjectFactory实现。
plugin
插件机制是mybatis很强大的功能,mybatis预留了插件的接口,让Mybatis更容易扩展。
environment
environment标签用来管理数据库的环境,比如我们可以有开发环境、测试环境、生产数据库。(其实就是管理数据源的东西)
transactionManager
如果配置成JDBC,则会使用Connection对象的Commit()、rollback()、close()管理事务。
如果配置为managed,会把事务交给容器管理,比如JBOSS、WEBLOGIC。我们跑的如果是本地环境,如果配置为mannaged就没有事务。
dataSource
数据源即是数据的来源,一个数据源就对应一个数据库。在java里面是他是对数据库连接的一个抽象。
一般的数据源都会包括连接池管理的功能,所以很多时候也把DataSource直接成为连接池,也就是带连接池的数据源。
为什么要用连接池?
除了连接池之外,大家应该也听过很多其他的池,比如线程池、内存池、对象池,池化技术的目的大多一样。
如果没有连接池,那么每个用户、每次会话连接数据库都需要直接创建和释放连接,这个过程是需要消耗时间和资源的,并且会消耗应用和服务器的性能。
如果采用池化技术,在应用程序里关闭连接的时候,物理连接比并没有被关闭,而是回到连接池里面。
从这个角度来考虑,一般的连接池都会有初始连接数、最大连接数、回收时间等等,提供提前创建、资源重用、数量控制、超时管理等等。
Mybatis自带两种数据源,unpooled和pooled,也可以配置成其他数据库,比如C3P0、HIkari等等。市面上流行的数据源,一般都有连接池功能。
在跟Spring集成的时候,事务和数据源都会交给Spring来管理,不再使用Mybatis配置的数据源。
mapper
<mapper>标签配置的是映射器,也就是Mapper.xml的路径,这些配置的目的就是在mybatis启动的时候,去扫描这些映射器,创建映射关系。
有四种表达方式:
1.使用相对与类路径的资源引用(resource)
<mappers>
<mapper resource="xxxMapper.xml">
</mappers>
2.使用完全限定资源定位符(绝对路径)(URL)
<mappers>
<mapper resource="file://app/xx/mappers/xxxMapper.xml">
</mappers>
3.使用映射器接口实现类的完全限定类名
<mappers>
<mapper class="com.dia.xx.mapper.xxxMapper">
</mappers>
4.将包内的映射器接口实现全部注册为映射器(最常用)
<mappers>
<mapper class="com.dia.xx.mapper">
</mappers>
批量插入以及动态SQL还有原生的插件等等
这些东西都是日常使用的,直接进入官网。
网友评论