暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Mybatis_06主配置文件的扩展

敲代码的人 2019-08-11
181

Mybatis中主配置文件的一些基本标签之前已经说明一些,参照


Mybatis_05


写在这章的开头:

对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 {


@Override
public void setParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
//Object o 是一个传入的java类型
if(o == null){
//如果传入的是空,把对应的位置设置为0
preparedStatement.setInt(i,0);
return;
}
boolean flag = (boolean)o;
int value = flag ? 1: 0;
preparedStatement.setInt(i,value);
}


@Override
public Object getResult(ResultSet resultSet, String columnName) throws SQLException {
int flag = resultSet.getInt(columnName);//1 or 0
boolean obj = Boolean.FALSE;
if(flag == 1){
obj = Boolean.TRUE;
}
return obj;
}


@Override
public Object getResult(ResultSet resultSet, int i) throws SQLException {
return null;
}


@Override
public 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 {


@Override
public 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固有的逻辑。打个比方,对于ExecutorMybatis中有几种实现:BatchExecutorReuseExecutorSimpleExecutorCachingExecutor。这个时候如果你觉得这几种实现对于Executor接口的query方法都不能满足你的要求,那怎么办呢?是要去改源码吗?当然不。我们可以建立一个Mybatis拦截器用于拦截Executor接口的query方法,在拦截之后实现自己的query方法逻辑,之后可以选择是否继续执行原来的query方法。

对于拦截器Mybatis为我们提供了一个Interceptor接口,接口如下图:

可以看到在该接口中一共定义有三个方法,interceptpluginsetPropertiesplugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法,当然也可以调用其他方法,这点将在后文讲解。setProperties方法是用于在Mybatis配置文件
指定一些属性的。

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


intercept::


定义自己的Interceptor最重要的是要实现plugin方法和intercept方法,在plugin方法中我们可以决定是否要进行拦截进而决定要返回一个什么样的目标对象。而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 {


@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("执行自定义拦截器开始====>>>>>>>>>>>>>>");
//本该执行的方法
Object resutl = invocation.proceed();
System.out.println("执行自定义拦截器结束====>>>>>>>>>>>>>>");
return resutl;
}


@Override
public Object plugin(Object o) {
return Plugin.wrap(o,this);
}


@Override
public 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 数据库厂商标识

不具体说明了,没啥用






文章转载自敲代码的人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论