项目路径:https://share.weiyun.com/5oetQPG
MyBatis配置项的顺序不能颠倒,如果颠倒了它们的顺序,则启动阶段就会发生异常,导致程序无法运行。
使用property子元素:
这样定义一次就可以到处引用了,如${database.username},但要定义的参数太多,则使用properties文件。
使用properties文件:
定义jdbc.properties文件
使用properties的属性resource来引入properties文件:
解密用户名和密码后创建SqlSessionFactory
满足运维人员对用户名和密码,保密的需求。
配置的优先级:程序传递方式 > properties文件的方式 > 使用property子元素的方式。
settings设置:
是MyBatis中最复杂的配置,能深刻影响MyBatis底层的运行,经常修改的一些规则:
自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器类型。
配置是否启动缓存:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
typeAliases别名:
系统定义别名:
在MyBatis中别名由类TypeAliasRegistry去定义。
一般对基本类型int定义的别名为,_int;类或包装器类定义的别名为,String为string、List为list、Long为long。
通过Configuration获取TypeAliasRegistry类对象
其中的,registerAlias方法去注册别名;getTypeAliasRegistry方法获取别名。
自定义别名:
可以支持扫描别名,自动将类名的第一个字母变为小写,作为别名。
若出现冲突,则使用注解进行区分:
typeHandler类型转换器:
在JDBC中,需要在PreparedStatement对象中设置那些,已经预编译过的SQL语句的参数。
执行SQL后,通过ResultSet对象,获取得到数据库的数据。
在typeHandler中,分为jdbcType和javaType,typeHandler的作用使其两个类型相互转换。
系统定义的typeHandler:
一般无须显式声明jdbcType和javaType,MyBatis会自己探测。
但一般枚举类需要自己去编写规则。
在MyBatis中typeHandler都要实现接口:org.apahe.ibatis.type.TypeHandler
系统注册typeHandler:
一般自定义的typeHanlder不用代码注册,而是通过配置或扫描。
自定义typeHandler:
在xml中配置typeHandler:
启用typeHandler的方式:
要么指定了与typeHandler一致的jdbcType和javaType,要么直接指定typeHandler具体实现类。
若一些数据从数据返回为空,则采用指定与typeHandler一致的jdbcType和javaType的方法,避免系统不知采用哪个typeHandler处理,从而产生异常。
若typeHandler采用包扫描,则无法指定具体的jdbcType和javaType,可使用注解的方式配置:
枚举typeHandler:
绝大多数情况下,typeHandler因为枚举而使用。
MyBatis已经定义了两个类来支持,枚举类型,但一般作用不大:
EnumOrdinalTypeHandler是根据,枚举数组小标索引的方式,进行匹配的。
它要求数据库返回一个整数(可以是以varchar作为类型的整数)作为其下标,根据下标找到对应的枚举类型。
EnumTypeHandler会把使用的名称转化为对应的枚举。
如,数据库返回的字符串“MALE”,进行Enum.valueOf(SexEnum.class,"MALE");转换。
但是使用是上述两种方式,会带来很大的局限性,一般自定义枚举typeHandler。
文件操作:
若数据库数据类型为blog,java数据类型为byte[],则使用BlogTypeHandler。
在现实中,一次性地将大量数据加载到JVM中,会给服务器带来很大压力。
因此,一般使用文件流的形式,所以,
数据库数据类型为blog,java数据类型为InputStream,则使用BlogInputStreamTypeHandler
因为性能不佳,大型互联网的网站会采用文件服务器的形式。
ObjectFactory(对象工厂):
创建结果集时,会使用一个对象工厂来完成创建这个结果集实例。
一般情况,MyBatis会使用其定义的对象工厂——DefaultObjectFactory。
若要自定义对象工厂,则继承DefaultObjectFactory:
public class MyObjectFactory extends DefaultObjectFactory {
Logger log = Logger.getLogger(MyObjectFactory.class);
private Object temp = null;
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
log.info("初始化参数:【" + properties.toString() + "】");
}
//方法二
@Override
public <T> T create(Class<T> type) {
T result = super.create(type);
log.info("创建对象:" + result.toString());
log.info("是否和上次创建的是同一个对象:【" + (temp == result) + "】");
return result;
}
//方法一
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
T result = super.create(type, constructorArgTypes, constructorArgs);
log.info("创建对象:" + result.toString());
temp = result;
return result;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
}
最后在配置文件对对象工厂进行配置:
运行结果是,先调用方法一,后调用方法二。
插件:
插件是MyBatis中最强大和灵活的组件,同时也是最复杂、最难以使用的组件,而且它十分危险。
应用它之前,一定要掌握底层的构成和运行原理。
environment(运行环境):
主要的作用是,配置数据库信息,可配置多个数据库。
下面又分为两个可配置的元素:事务管理器、数据源。
实际工作中,会采用Spring对数据源和数据库的事务进行管理。
transactionManager(事务管理器):
transactionManager提供了两个实现类,需要实现接口Transaction(提交、回滚、关闭等数据库事务方法)
因此也对应两个相应的工厂类,需实现TransactionFactory接口。
于是可把事务管理器配置成如下方式:
JDBC:以JDBC的方式对数据库的提交和回滚进行操作。
MANAGED:把事务交给容器处理,可对closeConnection属性设置为false,来阻止它默认关闭。
自定义事务工厂:需实现TransactionFactory接口
自定义了事务工厂,还需通过实现Transaction接口,自定义事务类,就能够通过自定义事务规则,满足需要。
environment数据源环境:
存在三个数据源工厂:
PooledDataSourceFactory、UnpooledDataSourceFactory、JndiDataSourceFactory。
JndiDataSourceFactory会根据JNDI的信息,拿到外部容器实现的数据库连接对象。
三个数据源工厂,最后生成的产品都会是一个实现了DataSource接口的数据库连接对象。
UNPOOLED:采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,所以创建比较慢。传递属性给数据库驱动是个可选项,且属性的前缀为“driver”。
POOLED:利用“池”的概念将JDBC的Connection对象组织起来,省去了创建新的连接实例时的初始化和认证时间。
JNDI:为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
该数据源有两个属性可配置:
initial_context:用来在InitialContext中寻找上下文,如initialContext.lookup(initial_context)。
data_source:引用数据源实例位置上下文的路径。
可通过添加前缀“env.”直接把属性传递给初始化上下文InitialContext。
MyBatis也支持第三方数据源,例如使用DBCP数据源,则需提供一个自定义DataSourceFactory:
在配置文件中,进行相应的配置:
databaseIdProvider数据库厂商标识:
使用系统默认的databaseIdProvider:
name是数据库名称,可通过connection.getMetaData().getDatabaseProductName()获取。
value是一个别名,可在mapper文件中,标识一条SQL语句适用于哪种数据库。
当databaseIdProvider属性被配置时,系统会优先取到和数据库配置一致的SQL,若没有databaseId的SQL,则把它当作默认值。如果还是取不到,则会抛出异常。
不使用系统规则:
自定义DatabaseIdProvider:
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private static final String DATEBASE_TYPE_DB2 = "DB2";
private static final String DATEBASE_TYPE_MYSQL = "MySQL";
private static final String DATEBASE_TYPE_ORACLE = "Oralce";
private Logger log = Logger.getLogger(MyDatabaseIdProvider.class);
@Override
public void setProperties(Properties props) {
log.info(props);
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
Connection connection = dataSource.getConnection();
String dbProductName = connection.getMetaData().getDatabaseProductName();
if (MyDatabaseIdProvider.DATEBASE_TYPE_DB2.equals(dbProductName)) {
return "db2";
} else if (MyDatabaseIdProvider.DATEBASE_TYPE_MYSQL
.equals(dbProductName)) {
return "mysql";
} else if (MyDatabaseIdProvider.DATEBASE_TYPE_ORACLE
.equals(dbProductName)) {
return "oracle";
} else {
return null;
}
}
}
setProperties方法可以读取配置的参数;getDatabaseId方法的返回值,返回去配置相应的SQL语句。
引入映射器的方法:
用文件路径引入:
用包名引入:
用类注册引入: