逻辑关系
具体代码
package com.sinbxeunha.josechan;
import com.mysql.cj.jdbc.*;
import com.mysql.jdbc.Driver;
import com.sinbxeunha.josechan.entity.User;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.executor.result.DefaultResultHandler;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.*;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.junit.Test;
import org.slf4j.impl.StaticLoggerBinder;
import org.slf4j.impl.StaticMDCBinder;
import javax.naming.Reference;
import javax.sql.DataSource;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
//@SpringBootTest
//@RunWith(SpringRunner.class)
public class MysqlTest {
// 通过最基础的jdbc链接请求mysql
@Test
public void jdbcConnection() throws Throwable {
// 加载mysql驱动
// 知识点一、Class.forName会调用类加载器实例化指定类(单例模式)
// 而com.mysql.cj.jdbc.Driver在实例化时将执行自身的静态代码块
// 从而将驱动注入到DriverManager中,这样DriverManager才知道用的是mysql的驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//链接mysql
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xss?user=root&password=Chenyu-524233828");
//生成statement
PreparedStatement statement = connection.prepareStatement("select * from user limit 2;");
//查询
ResultSet set = statement.executeQuery();
Map<Integer, User> userMap = new HashMap<Integer, User>();
User user = null;
//循环遍历结果,记录到map中
while (set.next()) {
user = new User();
int id = set.getInt("id");
user.setId(id);
user.setPassword(set.getString("password"));
user.setUsername(set.getString("username"));
userMap.put(id, user);
}
System.out.println(userMap.toString());
}
// 通过jdbc的链接池请求数据库
@Test
public void jdbcDataSource() throws Throwable {
// 链接池的工厂
MysqlDataSourceFactory factory = new MysqlDataSourceFactory();
// 工厂生成链接池对象时需要知道用的是哪个链接池
Reference reference = new Reference("com.mysql.cj.jdbc.MysqlDataSource");
// 获得链接池
MysqlDataSource dataSource = (MysqlDataSource) factory.getObjectInstance(reference, null, null, null);
// 配置mysql地址
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/xss?user=root&password=Chenyu-524233828");
//从链接池获取链接
Connection connection = dataSource.getConnection();
// 后面就跟上面一样
PreparedStatement statement = connection.prepareStatement("select * from user limit 2;");
ResultSet set = statement.executeQuery();
Map<Integer, User> userMap = new HashMap<Integer, User>();
User user = null;
while (set.next()) {
user = new User();
int id = set.getInt("id");
user.setId(id);
user.setPassword(set.getString("password"));
user.setUsername(set.getString("username"));
userMap.put(id, user);
}
System.out.println(userMap.toString());
}
// 使用带有XA事务的链接请求mysql
@Test
public void jdbcXADataSource() throws Throwable {
// 同样是链接池工厂
MysqlDataSourceFactory factory = new MysqlDataSourceFactory();
// 这次用MysqlXADataSource链接池
Reference reference = new Reference("com.mysql.cj.jdbc.MysqlXADataSource");
// 获得链接池对象
MysqlXADataSource dataSource = (MysqlXADataSource) factory.getObjectInstance(reference, null, null, null);
//配置mysql链接
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/xss?user=root&password=Chenyu-524233828");
// 用普通链接
// Connection connection = dataSource.getConnection();
// 用XAConnection
MysqlXAConnection xaConnection = (MysqlXAConnection) dataSource.getXAConnection();
// //读取数据库当前的xa事务的标识,实际使用时如果要生成新的Xid,需要确保xid没有被当前数据库占用
// // 因此需要获得当前的xid,用以进行对比,这里图方便,就不比较了
// Xid[] xids = xaConnection.recover(XAResource.TMSTARTRSCAN);
//生成自己的Xid
byte[] gtrid = new byte[1];
byte[] bqual = new byte[1];
MysqlXid xid = new MysqlXid(gtrid, bqual, 1);
//开启事务
xaConnection.getXAResource().start(xid, XAResource.TMNOFLAGS);
//执行
PreparedStatement statement = xaConnection.getConnection().prepareStatement("update user set username='pabo' where id=1");
int set = statement.executeUpdate();
xaConnection.getXAResource().end(xid, XAResource.TMSUCCESS);
int ret = xaConnection.getXAResource().prepare(xid);
if (ret == XAResource.XA_OK) {
xaConnection.getXAResource().commit(xid, false);
} else {
xaConnection.getXAResource().rollback(xid);
}
System.out.println(set);
}
// 通过mybatis请求数据库
@Test
public void MybatisSqlSession() throws Throwable {
// SqlSession工厂的建造者
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 配置项
Configuration configuration = new Configuration();
// 链接池的工厂
PooledDataSourceFactory pooledDataSourceFactory= new PooledDataSourceFactory();
//链接池的配置
Properties properties = new Properties();
properties.setProperty("driver", "com.mysql.cj.jdbc.Driver");
properties.setProperty("url", "jdbc:mysql://127.0.0.1:3306/xss?user=root&password=Chenyu-524233828");
//配置工厂
pooledDataSourceFactory.setProperties(properties);
// 创建链接池
DataSource dataSource = pooledDataSourceFactory.getDataSource();
// 创建环境
Environment environment = new Environment("development", new JdbcTransactionFactory(), dataSource);
configuration.setEnvironment(environment);
// 建造工厂
SqlSessionFactory factory = builder.build(configuration);
// 创建SqlSession
SqlSession session = factory.openSession();
//请求数据库
PreparedStatement statement = session.getConnection().prepareStatement("select * from user limit 2;");
ResultSet set = statement.executeQuery();
Map<Integer, User> userMap = new HashMap<Integer, User>();
User user = null;
while (set.next()) {
user = new User();
int id = set.getInt("id");
user.setId(id);
user.setPassword(set.getString("password"));
user.setUsername(set.getString("username"));
userMap.put(id, user);
}
System.out.println(userMap.toString());
}
//使用SessionManager,和上面基本一样
@Test
public void MybatisSqlSessionManager() throws Throwable{
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
Configuration configuration = new Configuration();
// 创建链接池
PooledDataSourceFactory pooledDataSourceFactory= new PooledDataSourceFactory();
//链接池的配置
Properties properties = new Properties();
properties.setProperty("driver", "com.mysql.cj.jdbc.Driver");
properties.setProperty("url", "jdbc:mysql://127.0.0.1:3306/xss?user=root&password=Chenyu-524233828");
pooledDataSourceFactory.setProperties(properties);
DataSource dataSource = pooledDataSourceFactory.getDataSource();
// 创建环境
Environment environment = new Environment("development", new JdbcTransactionFactory(), dataSource);
configuration.setEnvironment(environment);
SqlSessionFactory factory = builder.build(configuration);
SqlSessionManager sessionManager = SqlSessionManager.newInstance(factory);
//请求数据库
PreparedStatement statement = sessionManager.openSession().getConnection().prepareStatement("select * from user limit 2;");
ResultSet set = statement.executeQuery();
Map<Integer, User> userMap = new HashMap<Integer, User>();
User user = null;
while (set.next()) {
user = new User();
int id = set.getInt("id");
user.setId(id);
user.setPassword(set.getString("password"));
user.setUsername(set.getString("username"));
userMap.put(id, user);
}
System.out.println(userMap.toString());
}
// 使用mapper来查询
@Test
public void mybatisMapper() throws Throwable{
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
Configuration configuration = new Configuration();
// 创建链接池
PooledDataSourceFactory pooledDataSourceFactory= new PooledDataSourceFactory();
//链接池的配置
Properties properties = new Properties();
properties.setProperty("driver", "com.mysql.cj.jdbc.Driver");
properties.setProperty("url", "jdbc:mysql://127.0.0.1:3306/xss?user=root&password=Chenyu-524233828");
pooledDataSourceFactory.setProperties(properties);
DataSource dataSource = pooledDataSourceFactory.getDataSource();
// 创建环境
Environment environment = new Environment("development", new JdbcTransactionFactory(), dataSource);
configuration.setEnvironment(environment);
// mapper
// mapper的类
String namespace = "com.sinbxeunha.josechan.repository.UserRepository";
// 读取继承这个类下面的所有类
Class<?> boundType = Resources.classForName(namespace);
// 这句意义不明,看官方注释说是用来给spring用的,因为有可能spring容器没有解析到这个类,所以需要标注出来
configuration.addLoadedResource("namespace:" + namespace);
// 添加mapper
configuration.addMapper(boundType);
SqlSessionFactory factory = builder.build(configuration);
// 创建sqlsession
SqlSession session = factory.openSession();
// 获取查询结果处理器
DefaultResultHandler resultHandler = new DefaultResultHandler();
// 查询,并且将结果放到处理器中
session.select("com.sinbxeunha.josechan.repository.UserRepository.selectUsers", resultHandler);
// 从处理器读取数据
List<Object> list = resultHandler.getResultList();
System.out.println(list);
}
}
spring如何使用的mybatis
一直有一个疑问,为什么spring只需要将SqlSessionFactory解析到容器中,并且使用@MapperScan扫描接口就能够用mapper查询数据库?
@Configuration
public class MybatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(@Autowired DataSource dataSource) throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
TransactionFactory transactionFactory = new SpringManagedTransactionFactory();
sqlSessionFactoryBean.setTransactionFactory(transactionFactory);
return sqlSessionFactoryBean.getObject();
}
}
下面是关键源码
1、MapperScan扫描时会告诉spring框架,如果有人实例化mapper接口时,调用MapperFactoryBean.getObject()获取实例
/** org.mybatis.spring.mapper.ClassPathMapperScanner.java **/
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
//……
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
//……
//关键是这句话
definition.setBeanClass(this.mapperFactoryBeanClass);
//……
}
}
又由于MapperFactoryBean继承自SqlSessionDaoSupport,该类需要注入一个SqlSessionFactory对象,因此当实例化mapper接口时,会将你解析到容器的工厂注入
/** MapperFactoryBean.java **/
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
并且调用getObject对象时,将会调用getSqlSession,通过注入的工厂实例化SqlSession,并调用它的getMapper方法,而这个getMapper方法,实际上调用了MapperRegistry的getMapper方法,对接口进行了代理,最终返回了一个接口的代理对象
而当你调用mapper的方法进行查询时,将调用MapperProxy对象的invoke方法
/** MapperProxy.java **/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}