一.spring和mybatis整合需要导入的依赖:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<!--mybatis的依赖-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--mybatis和spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!--spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<dependency>
<!-- spring操作数据库 -->
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<!--阿里的连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
1.实体User,创建实体类,构造,getset
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TSupplier {
private Integer id;
private String supCode;
private String supName;
private String supDesc;
private String supContact;
private String supPhone;
private String supAddress;
private String supFax;
private Integer createdUserId;
private Date createdTime;
private Integer updateUserId;
private Date updatedTime;
}
2.实体别名,在mybatis-config.xml中,为User添加别名,这样在Mapper映射文件中,我们就可以不用写实体的全限定名,用别名就行了。
表,需要在数据库中创建user表,表中应有与实体属性对应的字段
二,SqlSessionFactoryBean
底层执行以下步骤:
数据源配置:使用setDataSource()方法设置数据源,指定数据库连接信息。数据源可以是任何实现了javax.sql.DataSource接口的对象,如Apache Commons DBCP、C3P0或HikariCP等。
配置文件加载:使用setConfigLocation()方法设置MyBatis的配置文件的位置。配置文件通常是mybatis-config.xml,包含了全局的MyBatis配置信息,例如数据库连接池、事务管理器、类型别名等。SqlSessionFactoryBean会加载并解析该配置文件。
Mapper文件加载:使用setMapperLocations()方法设置Mapper接口对应的XML映射文件的位置。可以指定一个或多个Mapper文件,这些文件定义了SQL语句与Java方法的映射关系。SqlSessionFactoryBean会加载这些Mapper文件并与配置文件进行整合。
MyBatis配置:SqlSessionFactoryBean会根据配置文件和Mapper文件的定义生成一个org.apache.ibatis.session.Configuration对象。这个Configuration对象包含了MyBatis的所有配置信息,如类型别名、插件、拦截器等。
创建SqlSessionFactory:使用上述Configuration对象创建一个SqlSessionFactory实例。SqlSessionFactory是MyBatis的核心接口,负责创建SqlSession对象,它是与数据库交互的入口点。
额外配置:SqlSessionFactoryBean还提供了其他一些可选的配置方法,如setTypeAliasesPackage()设置类型别名的包路径,setPlugins()设置插件等。这些配置方法可以根据需求进行调整。
最终,SqlSessionFactoryBean会在初始化过程中加载配置文件、Mapper文件,并根据配置信息创建一个SqlSessionFactory实例。这个SqlSessionFactory可以被SqlSessionDaoSupport或者MyBatis的SqlSessionUtils等类使用,用于创建和管理SqlSession实例,从而执行数据库操作。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationContextAware {
private DataSource dataSource; // 数据源
private Resource configLocation; // MyBatis配置文件的位置
private String typeAliasesPackage; // 实体类的包路径
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // SqlSessionFactoryBuilder实例
private SqlSessionFactory sqlSessionFactory; // SqlSessionFactory实例
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}
@Override
public void afterPropertiesSet() throws Exception {
// 构建配置对象
Configuration configuration = sqlSessionFactoryBuilder.build(configLocation.getInputStream());
// 配置数据源
Environment environment = new Environment("default", new JdbcTransactionFactory(), dataSource);
configuration.setEnvironment(environment);
// 配置实体类别名
if (StringUtils.hasText(typeAliasesPackage)) {
TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
String[] packages = typeAliasesPackage.split(",");
for (String packageToScan : packages) {
typeAliasRegistry.registerAliases(packageToScan);
}
}
// 创建SqlSessionFactory实例
sqlSessionFactory = sqlSessionFactoryBuilder.build(configuration);
}
@Override
public SqlSessionFactory getObject() throws Exception {
return sqlSessionFactory;
}
@Override
public Class<?> getObjectType() {
return (this.sqlSessionFactory != null ? this.sqlSessionFactory.getClass() : SqlSessionFactory.class);
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// do nothing
}
}
三,MapperScannerConfigurer
MapperScannerConfigurer是一个后置处理器,用于自动扫描指定包路径下的Mapper接口,并将其注册为Spring的bean。它的底层实现如下:
实现接口:MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,该接口定义了在所有bean定义信息加载完成后、但在bean实例化之前触发的方法。这使得MapperScannerConfigurer能够在Spring容器加载bean定义时进行自定义的处理。
设置包路径:通过setBasePackage方法设置Mapper接口所在的包路径。可以使用通配符(如com.example.*.mapper)来指定多个包路径。
扫描Mapper接口:在postProcessBeanDefinitionRegistry方法中,MapperScannerConfigurer创建了一个ClassPathMapperScanner对象,并将指定的包路径传递给它。ClassPathMapperScanner是一个自定义的类,用于扫描指定包路径下的类文件。
注册Mapper接口:ClassPathMapperScanner会扫描指定包路径下的类,找到符合条件的类,例如标注了@Mapper注解的接口或符合命名规则的接口。然后,通过BeanDefinitionRegistry接口的registerBeanDefinition方法将这些类注册为Spring的bean。这些Mapper接口会以MapperFactoryBean的形式注册,MapperFactoryBean是一个特殊的Spring bean,用于创建Mapper接口的实例。
配置信息:MapperScannerConfigurer还提供了一些其他的配置选项,可以通过setAddToConfig方法设置是否将Mapper接口添加到MyBatis的配置中。如果设置为true,则会将这些Mapper接口添加到MyBatis的配置中,使得MyBatis能够自动管理这些Mapper接口的实例。
通过以上步骤,MapperScannerConfigurer能够自动扫描指定包路径下的Mapper接口,并将其注册为Spring的bean。这样,在需要使用Mapper接口的地方,可以直接通过@Autowired注解或其他方式进行依赖注入,从而方便地使用Mapper接口进行数据库操作。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware {
private String basePackage; // Mapper接口所在的包路径
private boolean addToConfig = true; // 是否将Mapper接口添加到MyBatis的配置中
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 扫描指定包路径下的Mapper接口
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(addToConfig);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// do nothing
}
@Override
public void afterPropertiesSet() throws Exception {
// 检查basePackage是否已设置
Assert.notNull(basePackage, "Property 'basePackage' is required");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// do nothing
}
}
四,BasicDataSource
主要涉及以下几个关键点:
连接池初始化:在BasicDataSource的构造方法中,会初始化连接池的一些参数,如初始连接数(initialSize)、最大连接数(maxTotal)、最大空闲连接数(maxIdle)和最小空闲连接数(minIdle)。同时,还会创建指定数量的数据库连接,放入连接池中。
初始化连接池的过程中,会根据配置的驱动类名(driverClassName)、数据库连接URL(url)、用户名(username)和密码(password)创建数据库连接。具体的过程是通过JDBC驱动类加载数据库驱动,然后通过DriverManager获取数据库连接。
连接获取:当调用getConnection方法时,会先检查空闲连接池(idleConnections)是否为空。如果为空,就会根据连接池的大小和最大连接数来决定是否创建一个新的数据库连接。
如果连接池未满,则创建一个新的连接,添加到连接池中并返回。创建连接的过程与连接池初始化时类似,通过JDBC驱动类加载数据库驱动,然后通过DriverManager获取数据库连接。
如果连接池已满,则抛出SQLException异常,表示连接池已满,无法获取新的连接。
如果空闲连接池不为空,则从空闲连接池中获取一个连接,并检查连接是否关闭。如果连接关闭,则创建一个新的连接替代原来的连接。这样可以避免使用已关闭的连接,保证连接的可用性。
连接回收:在使用完数据库连接后,需要将连接返回给连接池进行复用。在BasicDataSource中,会将连接放回空闲连接池中,以供后续的连接获取操作使用。
连接状态检查:在获取连接时,会检查连接是否关闭。如果连接已关闭,则会创建一个新的连接替代原来的连接。这样可以避免使用已关闭的连接,保证连接的可用性。
连接的创建和销毁:在连接池中,会根据需要创建和销毁数据库连接。创建连接时,会使用JDBC驱动类加载数据库驱动,然后通过DriverManager获取数据库连接。销毁连接时,会调用Connection的close方法关闭连接。
在销毁连接之前,还会检查连接是否已关闭。如果连接未关闭,则调用Connection的close方法关闭连接。
连接的销毁是为了释放资源,避免资源的浪费。
BasicDataSource还提供了其他一些方法,如设置日志输出流、设置登录超时时间等,以便进行连接池的管理和配置。
总结来说,BasicDataSource底层实现主要通过维护连接池和使用连接池管理数据库连接的获取和释放,以提高数据库操作的性能和效率。它提供了简单而灵活的接口,方便开发人员使用和配置数据库连接池。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware {
private String basePackage; // Mapper接口所在的包路径
private boolean addToConfig = true; // 是否将Mapper接口添加到MyBatis的配置中
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 扫描指定包路径下的Mapper接口
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(addToConfig);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// do nothing
}
@Override
public void afterPropertiesSet() throws Exception {
// 检查basePackage是否已设置
Assert.notNull(basePackage, "Property 'basePackage' is required");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// do nothing
}
}