一、四大核心组件:SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession,SQL Mapper;
二、组件生命周期
三、详解MyBatis配置:properties ,settings,typeAliases
从(一)的介绍可知,MyBatis有如下优点:
(1)不屏蔽SQL,这样我们就可以对SQL进行优化和改造,从而提供系统性能;
(2)提供强大、灵活的映射机制,动态SQL;
(3)提供Mapper的接口编程:接口+XML就能创建映射器,简化开发;
1.四大核心组件
(1)SqlSessionFactoryBuilder(构造器):根据XML配置或java代码生成SqlSessionFactory,采用的是分步构建Builder模式;SqlSessionFactoryBuilder提供了Configuration类作为引导;每个基于MyBatis的应用都以一个SqlSessionFactory的实例为中心,且SqlSessionFactory唯一的作用就是生成SqlSession,因此会采用单例模式;
MyBatis中的XML文件有两种,基础配置文件(最基本的上下文参数和运行环境)和映射文件(映射关系、SQL、参数等);
–使用XML构建SqlSessionFactory:配置在XML,便于日后修改与维护,推荐;
//基础配置文件:mybatis-config.xml
<?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>
<typeAliases>
<!-- 别名,role代表对应类Role,在MyBatis上下文就可以使用别名去代替全限定名了-->
<typeAlias alias="role" type="cn.infocore.mybatis.pojo.Role"/>
</typeAliases>
<!-- 数据库环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!--事务管理器,采用的是JDBC管理器方式-->
<dataSource type="POOLED"><!--POOLED代表采用MyBatis内部提供的连接池方式-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/xx_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件:映射器(XML文件或接口) -->
<mappers>
<mapper resource="cn/infocore/mybatis/mapper/RoleMapper.xml"/>
<mapper class="cn.infocore.mybatis.mapper.RoleMapper2"/>
</mappers>
</configuration>
String resource="mybatis-config.xml";
InputStream ins=Resource.getResourceAsStream(resource);
SqlSessionFactory factory=new SqlSessionactoryBuilder().build(ins);
–使用代码生成SqlSessionFactory:一般不推荐使用,除非特殊需求,如需要配置加密的数据库用户名和密码;
//数据库连接池信息
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://localhost:3306/chapter3");
dataSource.setDefaultAutoCommit(false);
//采用MyBatis的JDBC事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
//创建Configuration对象
Configuration configuration = new Configuration(environment);
//注册一个MyBatis上下文别名
configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
//加入一个映射器
configuration.addMapper(RoleMapper.class);
configuration.addMapper(RoleMapper2.class);
//使用SqlSessionFactoryBuilder构建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return sqlSessionFactory;
(2)SqlSessionFactory(工厂接口):使用工厂模式生成SqlSession;SqlSessionFactory有两个实现类:DefaultSqlSession(单线程环境)和SqlSessionManager(多线程);
创建SqlSession:SqlSession是一个门面接口,真正起作用的是Executor;
SqlSession session=sqlSessionFactory.openSession();
//session.commit();
//session.rollback();
//session.close();
(3)SqlSession(会话):MyBatis的核心接口对象,发送SQL并返回结果,获取Mapper的接口,掌控数据库事务;作用类似于一个Connextion对象,代表着一个连接资源的启用;
(4)SQL Mapper(映射器):Java接口+XML(注解)构成,需给出对应的SQL和映射规则,发送SQL(将POJO数据插入到数据库)并返回结构(将结果映射为一个POJO),配置缓存,提供动态SQL;注意,接口本身是不能直接运行,但MyBatis会运用动态代理技术为这个接口生成一个代理对象,由代理对象去处理相关逻辑,而这一切是MyBatis自动完成的;
–实现方式一:XML文件形式
//-----------接口
public interface RoleMapper {
public Role getRole(@Param("id") Integer id);
}
//----------mybatis-config.xml加入
<mapper resource="cn/infocore/mybatis/mapper/RoleMapper.xml"/>
//-----------RoleMapper.xml
<?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="cn.infocore.mybatis.mapper.RoleMapper"> <!--对应接口-->
<select id="getRole" parameterType="long" resultType="role">
<!--id是对应接口方法名,parameterType参数类型,resultType返回类型,对应配置文件的别名-->
select id, role_name as roleName, note from t_role where id = #{id}
</select>
</mapper>
只要SQL返回的列名与POJO属性名对应起来(命名一样),MyBatis默认会自动映射;
–实现方式二:注解,直接在接口上注入
public interface RoleMapper {
@Select("select t_id, t_name , t_note from t_role where t_id = #{id}")
public Role getRole(@Param("id") Integer id);
}
如果同时配置,方式一会覆盖方式二;推荐方式一;
----SqlSession发送SQL:命名空间加SQL ID组合,定位一条SQL;
Role role=(Role)session.selectOne("cn.infocore.mybatis.mapper.RoleMapper.getRole",1);
如果在MyBatis中只有一个id为getRole的SQL可以简写:
Role role=(Role)session.selectOne("getRole",1);
----用Mapper接口发送SQL
RoleMapper mapper=session.getMapper(RoleMapper.class);
Role role3=mapper.getRole(1);
对比:推荐使用Mapper接口发送SQL,因为使用接口可以消除SqlSession带来的功能性代码,提高可读性;且使用接口的方式会有IDE校验和报错,而SqlSession的定位是一个字符串,只有运行时才知道对不对;
2.生命周期
(1)SqlSessionFactoryBuilder:SqlSessionFactoryBuilder是用户创建SqlSessionFactory,因此只存在与创建SqlSessionFactory的方法中;
(2)SqlSessionFactory:可被认为是一个数据库连接池,作用是创建SqlSession接口对象,因此SqlSessionFactory存在于整个MyBatis应用中,等于MyBatis的生命周期,不建议存在多个,因为很容易导致数据库连接资源被消耗光;
(3)SqlSession:可被认为是一个数据库连接(Connection对象),生命周期是一个业务请求中,处理完,关闭连接close,再归还给SqlSessionFactory;
(4)Mapper:由SqlSession创建,所以生命周期最多和SqlSession一样;
3.详解MyBatis配置
<?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 /><!-- 属性 -->
<settings /><!-- 设置 -->
<typeAliases /><!-- 别名 -->
<environments> <!-- 配置环境 -->
<environment> <!-- 环境变量 -->
<transactionManager /> <!-- 事务管理器 -->
<dataSource /> <!-- 数据源 -->
</environment>
</environments>
<databaseIdProvider /><!-- 数据库厂商标识 -->
<mappers /> <!-- 映射器 -->
</configuration>
(1)properties :给系统配置一些运行参数,使用方式有三种:
–property子元素:适用于一处定义,多处引用${database.driver};
<properties>
<propertyname="database.driver" value="com.mysql.jdbc.Driver"></property>
</properties>
–使用properties文件:多处引用${database.driver}
<properties resource="jdbc.properties"/>
–程序代码:适用于需要特殊处理的,如保存的用户名密码需要在创建SqlSessionactory前先解密;
InputStream ins=Resource.getResourceAsStream("jdbc.properties");
Properties prop=new Properties();
props.load(ins);
String password=props.getProperty("database.password");
props.put("database.password",CodeUtils.decode(password));//解密
InputStream ins2=Resource.getResourceAsStream(resource);
SqlSessionFactory session=new SqlSessionFactoryBuilder.build(ins2,props);//覆盖
总结:最优选择程序代码,其次是properties文件,最后是property子元素,如都配置,前者会覆盖后者;
建议使用properties文件,因为比较简单且维护方便,但对于一些特殊要求的场景,那就参考程序代码;
(2)settings:主要MyBatis底层的一些配置,大部分情况下默认即可,先介绍一些常用规则:
<settings>
<!--所有映射器中配置缓存的全局开关-->
<setting name="cacheEnabled" value="true"/>
<!--延迟加载的全局开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--是否允许单一语句返回多结果集-->
<setting name="mutipleResultSetsEnabled" value="true"/>
<!--使用列标签代替列名-->
<setting name="useColumnLable" value="true"/>
<!--允许JDBC支持自动生成组件-->
<setting name="useGeneratedKeys" value="false"/>
<!--指定MyBatis应如何自动映射列到字段或属性:NONE表示取消自动映射;PARTIAL只会自动映射,没有定义嵌套结果集和映射结果集;FULL会自动映射任意复杂的结果集-->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!--指定自动映射当中未知列或未知属性类型的行为,默认不处理,只有当日志级别是WARN级别及以下才会显示相关日志,取值:NONE,WARNING,FAILING-->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<!--默认执行器,SIMPLE是普通,REUSE是重用预处理语句,BATCH是重用语句并执行批量更新-->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!--设置超时时间:驱动等待数据库响应的秒数-->
<setting name="defaultStatementTimeout" value="25"/>
<!--设置数据库驱动程序默认返回的条数限制-->
<setting name="defaultFetchSize" value="100"/>
<!--是否允许在嵌套语句中使用分页,false表示允许-->
<setting name="safeRowBoundsEnabled" value="false"/>
<!--是否开启驼峰命名规则映射,即从数据库列名A_Column到POJO属性aColumn-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!--MyBatis利用本地缓存机制防止循环引用和加速重复嵌套查询,默认SESSION表示缓存一个会话中执行的所有查询;
STATEMENT表示本地会话金庸在语句执行上,对相同SqlSession的不通调用将不会共享数据-->
<setting name="localCacheScope" value="SESSION"/>
<!--当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型,多数情况下使用一般类型即可,如NULL、VARCHAR、OTHER-->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!--指定哪个对象的方法触发一次延迟加载,可覆盖cacheEnabled全局-->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
(3)typeAliases别名:在类的全限定名称很长且需要大量使用时,允许定义一个简写来代表这个类,就是别名;分系统定义别名和自定义别名;别名不区分大小写;
MyBatis初始化时,系统自动初始化了一些别名,如下:
public TypeAliasRegistry() {
//支持数据:别名[]
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
//以下不支持数组
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
以上就是系统定义的别名,我们使用时,要注意不要重名;自定义别名针对自己定义的对象,可以使用TypeAliasRegistry的registerAlias方法注册,也可以用配置文件或扫描方式;单个注册很简单,之前的代码里有提到过,主要说一下批量注册:
<typeAliases>
<package name="cn.infocore.mybatis.pojo"/>
</typeAliases>
表示MyBatis将扫描这个包里的类,将其第一个字母变小写作为其别名,不过同时扫描多个包时,可能会出现重名的情况,这个时候就需要使用不同的@Alias(“xxx”)来区分;
@Alias("xxx1")
public Class xxx{}