一、mybatis使用方式
mybatis使用需要创建SqlSessionFactory,创建方式有两种:
- 通过mybatis自己提供的SqlSessionFactoryBuilder创建:
1.1通过代码创建,不需要配置xml
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
//配置Java Mapper文件的包名
configuration.addMappers("com.xiaolong.dao");
Environment environment = new Environment("dev",new ManagedTransactionFactory(),dataSource());
configuration.setEnvironment(environment);
return builder.build(configuration);
}
1.2 通过配置文件configration.xml创建:
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
return builder.build(Resources.getResourceAsReader("mybatisConfig.xml"));
}
mybatisConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<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/test?useSSL=false" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
//配置mapper文件
<mapper resource="mappers/studentMapper.xml"/>
</mappers>
</configuration>
2.通过spring创建,mybatis-spring.jar提供了SqlSessionFactoryBean
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//configuration配置文件
ClassPathResource classPathResource = new ClassPathResource("mybatisConfig.xml");
sqlSessionFactoryBean.setConfigLocation(classPathResource);
sqlSessionFactoryBean.setDataSource(dataSource());
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
(1)扫描mapper.xml的路径
Resource[] resources = resolver.getResources("classpath*:*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
(2)扫描java Mapper类的路劲
sqlSessionFactory.getConfiguration().addMappers("com.xiaolong.dao");
return sqlSessionFactory;
}
(1)和(2)时配置Mapper文件的两种方式,第一种方式是直接扫描xml
文件,xml文件的路劲可以随便配置,而不需要java文件,第二种方式是配置java Mapper,通过Java Mappper 找到的mapper.xml
(1)第一种使用方式
studentMapper.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="com.xiaolong.dao.StudentDao">
<select id="selectById" parameterType="int" resultType="com.xiaolong.dto.Student">
select * from t_student where id = #{id}
</select>
</mapper>
使用
@Transactional
@Service
public class MybatisStudentService {
@Autowired
SqlSessionFactory sqlSessionFactory;
/**
* 使用xml方式,无Java dao文件
* @param id
* @return
*/
public Student selectById(int id){
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过字符串获取xml里的方法
Student student = sqlSession.selectOne("com.xiaolong.dao.StudentDao.selectById",6);
return student;
}
}
(2)第二种使用方式,只配置Java Mapper文件的位置,而不需要配置mapper.xml文件的位置,但是第二种方式也需要mapper.xml,那mapper.xml应该放哪呢?
@Transactional
@Service
public class MybatisStudentService {
@Autowired
SqlSessionFactory sqlSessionFactory;
/**
* 使用Java dao方式,先获取dao代理对象
* @param id
* @return
*/
public Student selectAllByJavaMapper(int id){
SqlSession sqlSession = sqlSessionFactory.openSession();
//其实看这里是通过java Mapper文件找的方法,那到底怎么和Mapper.xml关联起来呢?
StudentHaveJavaMapper studentMapper = sqlSession.getMapper(StudentHaveJavaMapper.class);
Student students = studentMapper.selectById(1);
return students;
}
}
StudentHaveJavaMapper.java
package com.xiaolong.dao;
import com.xiaolong.dto.Student;
import java.util.List;
public interface StudentHaveJavaMapper {
public List<Student> selectAll();
Student selectById(int id);
}
<?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="com.xiaolong.dao.StudentHaveJavaMapper">
<select id="selectAll" resultType="com.xiaolong.dto.Student">
select * from t_student
</select>
<select id="selectById" parameterType="int" resultType="com.xiaolong.dto.Student">
select * from t_student where id = #{id}
</select>
</mapper>
这里是把Mapper.xml放到了和Java文件相同的位置,而且必须得放到这里,且名称必须和Java文件一致了,原因是啥呢?
mybatis是如何找到xml文件的呢?我们从 上面使用的这行代码看看:
sqlSessionFactory.getConfiguration().addMappers(“com.xiaolong.dao”);
sqlSessionFactory.getConfiguration()是获取了Configuration对象,然后看addMappers()方法
Configuration.java
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
MapperRegistry.java
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
//这里通过packageName去找到所有的JAVA Mapper文件
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
//循环每个java Mapper文件去加载mapper.xml
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
//这里的type就是Java Mapper文件,比如我们例子里的StudentHaveJavaMapper.java
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperAnnotationBuilder.java
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//在这里去找xml文件
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
//在这里通过获取Java Mapper类的名称,比如com.xiaolong.dao.StudentHaveJavaMapper.java
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
在上方通过获取Java Mapper类的名称,比如com.xiaolong.dao.StudentHaveJavaMapper.java,然后替换成
com/xiaolong/dao/StudentHaveJavaMapper.xml,然后通过Resources.getResourceAsStream(type.getClassLoader(), xmlResource);去加载xml,然后在解析,这就是为啥第二种方式的xml文件必须和Java Mapper类放一起,且名称必须一致了。