springboot整合Mybatis之创建对象SqlSessionFactory和Sql。
。。
⼀、创建SqlSessionFactory和SqlSession对象
1.2.什么是SqlSession?
1.3.SqlSessionFactory和SqlSession实现过程(源码分析)
1.1什么是SqlSessionFactory对象?
SqlSessionFactory是Mybatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像。SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象类获得,⽽SqlSessionFactoryBuilder则可以从XML配置⽂件或者⼀个预先定制的Configuration的实例构建出SqlSessionFactory的实例。每⼀个Mybaits的应⽤给程序都以⼀个SqlSessionFactory对象的实例为核⼼,同时SqlSessionFactory也是线程安全的,SqlSessionFactory⼀旦被创建,应该在应⽤执⾏期间都存在,在应⽤运⾏期间不要重复创建多次,建议使⽤单例模式。SqlSessionFactory是创建SqlSession的⼯⼚。
SqlSessionFactory接⼝源码如下所⽰:
package org.apache.ibatis.session;
import java.sql.Connection;
/**
* Creates an {@link SqlSession} out of a connection or a DataSource
*
* @author Clinton Begin
*/
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
⼆、什么是SqlSession?
SqlSession是MyBatis的关键对象,它是应⽤程序与持久层之间交互操作的⼀个单线程对象,类似于JDBC中的Connection。SqlSession对象完全包含以数据库为背景的所有执⾏SQL操作的⽅法,它的底层封装了JDBC连接,可以⽤SqlSession实例来直接执⾏被映射的SQL语句。每个线程都应该有它⾃⼰的SqlSession实例。SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,绝对不能讲Sql
Seesion 实例的引⽤放在⼀个类的静态字段甚⾄是实例字段中。也绝不能将SqlSession实例的引⽤放在任何类型的管理范围中,⽐如Servlet当中的HttpSession对象中。使⽤完SqlSeesion之后关闭Session很重要,应该确保使⽤finally块来关闭它。⽰例代码有点多就不贴了,可以⾃⼰去看源码。
三、SqlSessionFactory和SqlSession实现过程
mybatis框架主要是围绕着SqlSessionFactory进⾏的,创建过程⼤概如下:
(1)、定义⼀个Configuration对象,其中包含数据源、事务、mapper⽂件资源以及影响数据库⾏为属性设置settings
(2)、通过配置对象,则可以创建⼀个SqlSessionFactoryBuilder对象
(3)、通过 SqlSessionFactoryBuilder 获得SqlSessionFactory 的实例。
(4)、SqlSessionFactory 的实例可以获得操作数据的SqlSession实例,通过这个实例对数据库进⾏操作
如果是spring+mybaits项⽬
profiles: devnew
datasource:
url: jdbc:mysql://x:80331/project?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
driver-class-name: sql.jdbc.Driver
username: xxxxxx
password: xxxxxx
redis:
host: x
port: 7000
password: xxxxxx
database: 7
lettuce:
pool:
max-active: 1000
max-wait: 10000ms
max-idle: 10
连接数据库:
/**
* 数据源配置
*/
@Configuration
public class DruidConfig {
private Logger logger = Logger(getClass());
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Bean
@Primary
public DataSource druidDataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(10);
datasource.setMinIdle(10);
datasource.setMaxActive(200);
datasource.setMaxWait(60000);
datasource.setTimeBetweenEvictionRunsMillis(45000);
datasource.setValidationQuery("SELECT 1");
datasource.setTestWhileIdle(true);
datasource.setTestOnBorrow(true);
datasource.setTestOnReturn(false);
datasource.setRemoveAbandonedTimeout(3600);
try {
datasource.setFilters("stat");
datasource.init();
} catch (SQLException e) {
<("druid configuration initialization filter", e);
}
return datasource;
}
}
SqlSessionFactory的创建:
public class SessionFactoryConfig implements TransactionManagementConfigurer {
@Autowired
private DataSource dataSource;
private String mapper = "classpath:com/xx/xxx/mapper/**/*l"; //xml扫描路径
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapper));
Object();
}
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new DataSourceTransactionManager(dataSource);
}
}
源码分析:
具体实现过程
第⼀步:我们看这个⽅法Object();
/**
* {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
接着我们在看 afterPropertiesSet();
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
接着我们在看看buildSqlSessionFactory()
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (figuration != null) {
configuration = figuration;
if (Variables() == null) {
configuration.figurationProperties);
} else if (figurationProperties != null) {
}
} else if (figLocation != null) {
xmlConfigBuilder = new InputStream(), null, figurationProperties); configuration = Configuration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); }
configuration = new Configuration();
if (figurationProperties != null) {
configuration.figurationProperties);
}
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}springframework和springboot
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!peAliases)) {
for (Class<?> typeAlias : peAliases) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (peHandlersPackage)) {
String[] typeHandlersPackageArray = peHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!peHandlers)) {
for (TypeHandler<?> typeHandler : peHandlers) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(DatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + figLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + figLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
if (ansactionFactory == null) {
}
configuration.setEnvironment(new vironment, ansactionFactory, this.dataSource)); if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
}
try {
XMLMapperBuilder xmlMapperBuilder = new InputStream(),
configuration, String(), SqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
到此⼀个SqlSessionFactory就创建好了。
1.4 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession⽣命周期
如果使⽤依赖注⼊框架(Spring)可不⽤在代码中关⼼作⽤域问题,因为依赖注⼊框架会为我们做好⼏乎⼀切⼯作。
SqlSessionFactoryBuilder
这个类可以被实例化、使⽤和丢弃,⼀旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳范围是⽅法范围(也就是局部⽅法变量)。你可以重⽤ SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其⼀直存在以保证所有的 XML 解析资源开放给更重要的事情。
SqlSessionFactory
SqlSessionFactory ⼀旦被创建就应该在应⽤的运⾏期间⼀直存在,没有任何理由对它进⾏清除或重建。使⽤ SqlSessionFactory 的最佳实践是在应⽤运⾏期间不要重复创建多次,多次重建 SqlSessionFactory 被视为⼀种bad smell。因此 SqlSessionFactory 的最佳范围是应⽤范围。有很多⽅法可以做到,最简单的就是使⽤单例模式或者静态单例模式。
SqlSession
每个线程都应该有它⾃⼰的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或⽅法范围。绝对不能将 SqlSession 实例的引⽤放在⼀个类的静态域,甚⾄⼀个类的实例变量也不⾏。也绝不能将 SqlSession 实例的引⽤放在任何类型的管理范围中,⽐如 Serlvet 架构中的 HttpSession。如果你现在正在使⽤⼀种 Web 框架,要考虑 SqlSession 放在⼀个和 HTTP 请求对象相似的范围中。换句话说,每次收到的 HTTP 请求,就可以打开⼀个 SqlSession,返回⼀个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执⾏关闭。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论