SM整合思路
1.引入相关依赖
spring mybatis mysql
2.如何整合?
Spring 项目管理框架 主要是用来负责组件对象的 创建 使用 销毁 对象创建
Mybatis 持久层 框架 主要是用来简化原始jdbc技术对数据库访问操作 操作数据库 mybatis中的核心对象---操作---数据库
整合思路: 通过spring框架接管mybatis框架中核心对象的创建
3.mybatis中的核心对象是哪些呢?
sqlSessionFactory Mybatis中的核心对象
4.SM整合spring&mybatis框架
即:通过spring框架接管mybatis框架中核心对象---sqlSessionFactory的创建
a)SqlSessionFactory:
首先思考:是简单对象还是复杂对象呢?
通过查看源码得知,SqlSessionFactory为接口类型的复杂对象
复杂对象如何创建呢?
@Override
public SqlSessionFactory getObject() throws Exception {
InputStream sq = Resources.getResourceAsStream("com/cf/sm/mybatis-config.xml");
return new SqlSessionFactoryBuilder().build(sq);
}
<bean id="sqlSessionFactoryBean" class="com.cf.sm.beanfactory.SqlSessionFactoryBean"></bean>
String mybatisConfigs;
public void setMybatisConfigs(String mybatisConfigs) {
this.mybatisConfigs = mybatisConfigs;
}
@Override
public SqlSessionFactory getObject() throws Exception {
InputStream sq = Resources.getResourceAsStream(mybatisConfigs);
return new SqlSessionFactoryBuilder().build(sq);
}
<bean id="sqlSessionFactoryBean" class="com.cf.sm.beanfactory.SqlSessionFactoryBean">
<property name="mybatisConfigs" value="com/cf/sm/mybatis-config.xml"></property>
</bean>
将mybatis主配置文件注入到sqlSessionFactoryBean中,更加灵活,以后更改配置文件不需要再改代码,只需在配置文件中中配置就可以了。
@Test
public void testSqlSessionFactoryBean() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/cf/sm/spring.xml");
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) context.getBean("sqlSessionFactoryBean");
System.out.println(sqlSessionFactory);
System.out.println(sqlSessionFactory.getClass().getName());
}
此时我们调用,就生成了mybatis的核心对象SqlSessionFactory。
虽然通过spring框架管理了mybatis的核心对象的创建,但是还不够,因为这意味着我们每一次使用mybatis,都需要去写一个SqlSessionFactoryBean类。于是,Mybatis官方为我们提供了一个封装好的jar包,可以更快捷的实现SqlSessionFactory对象的创建
5.mybatis-spring
引入依赖:mybatis-spring
引入依赖后,我们就可以在spring工厂中直接获取到封装在内的SqlSessionFactoryBean类。
此时,点开org.mybatis.spring.SqlSessionFactoryBean的源码,我们会发现,
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(SqlSessionFactoryBean.class);
private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
private Resource configLocation;
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
正是和我们手写的一样,这个类实现了FactoryBean接口,泛型为<SqlSessionFactory>。
其中变量private Resource configLocation;
我们点进去Resource的源码会发现
public interface Resource extends InputStreamSource
configLocation继承了InputStreamSource,此时再回顾我们之前手写的代码中
@Override
public SqlSessionFactory getObject() throws Exception {
InputStream sq = Resources.getResourceAsStream(mybatisConfigs);
return new SqlSessionFactoryBuilder().build(sq);
}
看到相似的对象类型:Resource--Resources | inputStreamSource--InputStream
个人猜测,org.mybatis.spring.SqlSessionFactoryBean源码中的configLocation变量正是我们手写代码中的mybatis配置文件地址。同样的,源码中也有创建了SqlSessionFactoryBuilder对象。正是我么之前手写的创建SqlSessionFactory对象的那一套代码。
于是,我把手写的bean换掉,换成了mybatis-spring jar包中的路径,并将configLocation属性的值注入,值为mybatis主配置文件的路径。
<bean id="mybatisConfigs" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="com/cf/sm/mybatis-config.xml"></property>
</bean>
尝试运行test类,却发现报错
四月 16, 2021 12:56:47 上午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mybatisConfigs' defined in class path resource [com/cf/sm/spring.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mybatisConfigs' defined in class path resource [com/cf/sm/spring.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:609)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:925)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:923)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at com.cf.sm.test.TestSqlSessionFactoryBean.testSqlSessionFactoryBean(TestSqlSessionFactoryBean.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalArgumentException: Property 'dataSource' is required
at org.springframework.util.Assert.notNull(Assert.java:201)
at org.mybatis.spring.SqlSessionFactoryBean.afterPropertiesSet(SqlSessionFactoryBean.java:470)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1847)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784)
... 79 more
报错信息大概意思就是:bean实例化失败,缺少必须属性dataSource。
那么,dataSource什么呢,看下我们的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 resource=""></properties>
<typeAliases>
<package name=""/>
</typeAliases>
-->
<!-- 用来配置自己数据库-->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sm_learn?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 注册mapper配置文件-->
<mappers></mappers>
</configuration>
mybatis配置文件分为两部分,一部分是与数据库的连接,一部分是各种类的映射。
而dataSource,也就是数据源,就是我们连接数据库的相关信息
<!-- 用来配置自己数据库-->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sm_learn?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
此时疑问:为啥我们把配置文件的路径都给框架了,而dataSource也是配置文件的一部分,为何还提示需要我们给他数据源呢?
原来,是mybatis-spring框架他不想要这个配置文件了,他对配置文件进行了更细的拆分,既然所有的配置文件都是两部分组成,我就把最深层的两部分信息拿到手不就完事了。配置文件整体是啥我不care,我也不想自己去里面解析dataSource和mapper,我要你直接拿给我。
6.创建数据源对象 druid
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
经过测试,DruidDataSource为简单对象类型,可以直接再工厂中创建bean
7.大功告成,成功启动
<!--创建数据源对象 druid C3p0 dbcp都是用来构建数据源的 引入依赖即可使用-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sm_learn?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 依赖数据源对象-->
<property name="dataSource" ref="dataSource"></property>
<!-- 依赖mapper文件对象-->
</bean>
数据源对象一定要有,mapper对象用到哪个加哪个就可以了。
在多次引用依赖之后,发现视频里的大佬引入依赖是会自动提示补齐,我的就没有,于是百度了一番也搞定了,今天太晚了,明天整理下。