Mybatis中主配置文件的一些基本标签之前已经说明一些,参照
写在这章的开头:
对mapper标签的补充说明
MyBatis中接口和对应mapper文件配置深入解析
首先要说明的问题是,Mybatis
中接口和对应的mapper文件不一定要放在同一个包下,放在一起的目的是为了Mybatis
进行自动扫描,并且要注意此时java接口的名称和mapper文件的名称要相同,否则会报异常,由于此时Mybatis会自动解析对应的接口和相应的配置文件,所以就不需要配置mapper文件的位置了。
默认MAVEN构建
如果在工程中使用了maven构建工具,那么就会出现一个问题:我们知道在典型的maven工程中,目录结构有:src/main/java
和src/main/resources
,前者是用来存放java源代码的,后者则是存放一些资源文件,比如配置文件等,在默认的情况下maven打包的时候,对于src/main/java
目录只打包源代码,而不会打包其他文件。所以此时如果把对应的mapper文件放到src/main/java
目录下时,不会打包到最终的jar文件夹中,也不会输出到target
文件夹中,由于在进行单元测试的时候执行的是/target
目录下/test-classes
下的代码,所以在测试的时候也不会成功。
如下情况

测试运行:

为了实现在maven默认环境下打包时,Mybatis的接口和mapper文件在同一包中,可以通过将接口文件放在src/main/java
某个包中,而在src/main/resources
目录中建立同样的包,这是一种约定优于配置的方式,这样在maven打包的时候就会将src/main/java
和src/main/resources
相同包下的文件合并到同一包中。
就是之前一直使用的方式

更改MAVEN配置
如果不想将接口和mapper文件分别放到src/main/java
和src/main/resources
中,而是全部放到src/main/java
,那么在构建的时候需要指定maven打包需要包括xml文件,具体配置如下:
修改pom文件
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>
这种形式的配置就可以用了

typeHandlers(类型处理器)
每当MyBatis 设置参数到PreparedStatement 或者从ResultSet 结果集中取得值时,就会使用TypeHandler 来处理数据库类型与java 类型之间转换。
下表描述了默认TypeHandlers

每当通过Mybatis执行数据的操作时,Mybatis会自动把Java类型和数据库中的类型做转换
也可以自己定义转换器(根据自己的要求转换数据)
例如:java的实体类中保存的是布尔类型,但是存入数据是一个标志,0表示false,1表示true
步骤:
1.先准备实体类,在实体类中添加一个flag字段,类型为布尔

2.对应的数据表也增加一个字段,类型为字段类型

3.实现Mybatis为用户提供自定义转换器的接口org.apache.ibatis.type.TypeHandler
重写其中的方法
public class MyTypeHandler implements TypeHandler {@Overridepublic void setParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {//Object o 是一个传入的java类型if(o == null){//如果传入的是空,把对应的位置设置为0preparedStatement.setInt(i,0);return;}boolean flag = (boolean)o;int value = flag ? 1: 0;preparedStatement.setInt(i,value);}@Overridepublic Object getResult(ResultSet resultSet, String columnName) throws SQLException {int flag = resultSet.getInt(columnName);//1 or 0boolean obj = Boolean.FALSE;if(flag == 1){obj = Boolean.TRUE;}return obj;}@Overridepublic Object getResult(ResultSet resultSet, int i) throws SQLException {return null;}@Overridepublic Object getResult(CallableStatement callableStatement, int i) throws SQLException {return null;}}
注:
setParameter方法:在生成sql语句的时候使用
getResult方法:查询结束之后,将resultSet数据行转换为实体类中对象时,通知TypeHandler将当前数据行的某个字段转换为何种类型
4.配置主配置文件的<typeHandlers></typeHandlers>标签,告诉当遇到某种数据类型才做转换,不然MyTypeHandler 接口的中object类型,哪知道用户要转换的是何种类型
<typeHandlers><!--handler 自定义拦截器的全限定类型--><!--javaType 实体中的类型--><!--jdbcType 数据库字段的类型--><typeHandler handler="com.huge.util.MyTypeHandler" javaType="Boolean" jdbcType="int"/></typeHandlers>
5.测试插入



6.测试查找


注意:当把自定义类配置在主配置文件中,那边作用针对的是所有的表
如果想要针对特定的表转换,需要在对应的sql映射文件中做配置
<resultMap id="AccountMap" type="Account"><result column="flag" property="flag" typeHandler="com.huge.util.MyTypeHandler"/></resultMap><select id="findOneById" parameterType="int" resultMap="AccountMap">select * from account where id = #{id}</select>
这里使用resultMap 标签,这个标签之后讲解,可以看到为特定的数据库中字段flag,和java中的flag字段做转换,如果全局和局部冲突,有限局部
objectFactory(对象工厂)
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。
Mybatis中DefaultObjectFactory实现了ObjectFactory,完成对查询回来数据的分装
如图:简单看下原码

自定义生产对象的工厂
1.故意改造实体类,多添加一个字段country,查询之后设置为china

2.创建自定义对象工厂继承DefaultObjectFactory
重写create一个方法就好了,这里简单点
public class MyObjectFactory extends DefaultObjectFactory {@Overridepublic Object create(Class type) {if(type == Account.class){Account account = (Account)super.create(type);System.out.println(account+"======================");account.setCountry("china");return account;}return super.create(type);}}org.apache.ibatis.plugin
3.配置全局的<objectFactory type=""></objectFactory>
<objectFactory type="com.huge.util.MyObjectFactory"></objectFactory>
4.测试查询

结果显示,经过这个类的时候,查询回来的数据还没有封装进入
plugins(插件)(也叫拦截器)
拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。打个比方,对于Executor,Mybatis中有几种实现:BatchExecutor、ReuseExecutor、SimpleExecutor和CachingExecutor。这个时候如果你觉得这几种实现对于Executor接口的query方法都不能满足你的要求,那怎么办呢?是要去改源码吗?当然不。我们可以建立一个Mybatis拦截器用于拦截Executor接口的query方法,在拦截之后实现自己的query方法逻辑,之后可以选择是否继续执行原来的query方法。
对于拦截器Mybatis为我们提供了一个Interceptor接口,接口如下图:

对于plugin方法而言,其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类,里面有一个静态方法wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。

intercept::

对于实现自己的Interceptor而言有两个很重要的注解,一个是@Intercepts,其值是一个@Signature数组。@Intercepts用于表明当前的对象是一个Interceptor,而@Signature则表明要拦截的接口(type )、方法(method )以及对应的参数类型()args 。来看一个自定义的简单Interceptor
@Intercepts({@Signature(method = "query",type = Executor.class,args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class MyInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("执行自定义拦截器开始====>>>>>>>>>>>>>>");//本该执行的方法Object resutl = invocation.proceed();System.out.println("执行自定义拦截器结束====>>>>>>>>>>>>>>");return resutl;}@Overridepublic Object plugin(Object o) {return Plugin.wrap(o,this);}@Overridepublic void setProperties(Properties properties) {}}
可以简单看下Executor这个接口:

主配置文件中加入<plugins>标签
<plugins><plugin interceptor="com.huge.util.MyInterceptor"/></plugins>
测试查询方法:

environments(环境信息集合)
MyBatis 可以配置多种环境。这会帮助你将 SQL 映射应用于多种数据库之中。但是要记得一个很重要的问题:你可以配置多种环境,但每个数据库对应一个 SqlSessionFactory。
所以,如果你想连接两个数据库,你需要创建两个SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,你就需要三个实例,以此类推。
为了明确创建哪种环境,你可以将它作为可选的参数传递给 SqlSessionFactoryBuilder。
可以接受环境配置的两个方法签名是:
SqlSessionFactory factory =sqlSessionFactoryBuilder.build(reader, environment);SqlSessionFactory factory =sqlSessionFactoryBuilder.build(reader,environment,properties);
如果环境被忽略,那么默认环境将会被加载,按照如下方式进行:
SqlSessionFactory factory =sqlSessionFactoryBuilder.build(reader);SqlSessionFactory factory =sqlSessionFactoryBuilder.build(reader,properties);
案列演示:在主配置文件中使用<environments> 配置了两个开发环境
<environments default="development"><!--配置环境1--><environment id="development1"><!-- 使用jdbc事务管理 --><transactionManager type="JDBC" /><!-- 数据库连接池 --><dataSource type="POOLED"><!--这里使用OGNL表达式去取,${这里面的就是properties小标签中name的值}--><property name="driver" value="${db.driver}" /><property name="url"value="${db.url}" /><property name="username" value="${db.username}" /><property name="password" value="${db.password}" /></dataSource></environment><!--配置环境2--><environment id="development2"><transactionManager type="JDBC" /><!-- 数据库连接池 --><dataSource type="POOLED"><!--这里使用OGNL表达式去取,${这里面的就是properties小标签中name的值}--><property name="driver" value="${db.driver}" /><property name="url"value="${db.url}" /><property name="username" value="${db.username}" /><property name="password" value="${db.password}" /></dataSource></environment></environments>
<environment id="development1">
<environment id="development2">
测试案例中
//创建SqlSessionFactory工厂SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"development2");
这里在补充一个标签,在environment标签之后
databaseIdProvider 数据库厂商标识
不具体说明了,没啥用




