请先熟知工厂模式和代理模式哦
core:
session工厂:
/**
* 一个连接对应一个会话,所以创建会话工厂
* 整合数据源,每次打开会话从数据源拿一个session
*/
public class SessionFactory {
//要有连接session才能生效哦
private DataSource dataSource;
/**
* 把UserMapper的配置文件加载进来形成env,有需要的就从env中拿
* 第一个String:namespace; 第二个String:方法名(saveUser,select..);
* DaoWrapper:拿到方法名标签中所有的内容比如resultype,paramType,sql语句...
*/
private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);
//拿mybatis配置文件的路径,需要dom4j解析xml文件
public SessionFactory(String config) {
loadXml(config);
}
// 打开一个会话
public Session openSession() {
Connection connection = null;
try {
connection = dataSource.getConnection(); //session拿到连接才能建立会话
} catch (SQLException e) {
e.printStackTrace();
}
return new Session(connection, env);
}
// 用dom4j依赖包 加载资源环境 解析数据源
public void loadXml(String config) {
try {
//解析xml文件
SAXReader reader = new SAXReader();
Document configDom = reader.read(Session.class.getClassLoader().getResourceAsStream(config));
Element configRoot = configDom.getRootElement();//拿到根节点
String dataSourceType = configRoot.element("dataSource").getTextTrim();
//赋值datasource
dataSource = DataSourceFactory.createDataSource(dataSourceType);
//获取所有的mapper文件
List mapperElements = configRoot.elements("mapper");
List<String> mapperPaths = new ArrayList<>();
for (Object element : mapperElements) {
Element mapper = (Element) element;
mapperPaths.add(mapper.getTextTrim());
}
for (String mapperPath : mapperPaths) {
// String:xml文件的方法名id,DaoWrapper:DaoWrapper中的成员
Map<String, DaoWrapper> wrapper = new HashMap<>(2);
Document document = reader.read(Session.class.getClassLoader().getResourceAsStream(mapperPath));
Element root = document.getRootElement();
String namespace = root.attribute("namespace").getValue();
Iterator iterator = root.elementIterator();
while (iterator.hasNext()) {
Element el = (Element) iterator.next();
String type = el.getName(); //什么样的sql语句,insert update or...
String id = el.attribute("id").getValue();
String resultType = el.attribute("resultType").getValue();
String paramType = el.attribute("paramType").getValue();
String sqlStr = el.getTextTrim(); //具体sql语句
// id是方法名,
wrapper.put(id, new DaoWrapper(type, resultType, paramType, sqlStr));
}
//xml文件里的namespace
env.put(namespace, wrapper);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
session:
**
* 通过session拿到一个代理对象,并操作会话
* 在session里用dao生成代理对象
* session就是客户端和服务端建立的会话,通过会话可以通过connection发sql
* 一个会话有一个connection,connection从数据源中来
*/
public class Session {
/**
* 每个会话持有一个链接
*/
private Connection connection;
/**
* 当前会话的上下文
*/
private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);
public Session(Connection connection,Map<String, Map<String, DaoWrapper>> env) {
this.connection = connection;
this.env = env;
}
/**
* 拿到一个包装类xxxWrapper(代理对象)
* @param clazz:也就是UserDao,传进来的代理对象。clazz.getName()就是拿到com.itnls.UserDao
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> clazz) {
T t = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{clazz},
new SQLHandler(connection, clazz,env.get(clazz.getName())));
return t;
}
// 开始会话
public void begin() {
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 提交
public void commit() {
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 回滚
public void rollback() {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection getConnection() {
return connection;
}
}
sql代理对象:
/**代理对象执行指定sql
*根据xml内容和接口对应的名字,执行相应实际的方法体
*/
public class SQLHandler implements InvocationHandler {
/**
* 需传入一个链接
*/
private Connection connection;
/**
* 需传入一个dao的类型
*/
private Class clazz;
/**
* 需传入一个独立的小环境,此时已经在namespace中了,就不用大Map套小map了
*/
private Map<String, DaoWrapper> env;
public SQLHandler(Connection connection, Class clazz, Map<String, DaoWrapper> env) {
this.connection = connection;
this.clazz = clazz;
this.env = env;
}
/**
* 生成代理DaoWrapper对象
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 根据方法名拿到包装,包装里有sql语句,参数类型,返回值类型,type(insert,select...)
DaoWrapper wrapper = env.get(method.getName());
// 预编译语句
PreparedStatement statement = connection.prepareStatement(wrapper.getSql());
// 对每一种sql语句进行独立的操作
if ("insert".equals(wrapper.getType())) {
String paramType = wrapper.getParamType();
// 暂定传入一个对象
Class<?> clazz = args[0].getClass();
Field[] fields = clazz.getDeclaredFields();
// 遍历所有字段
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
statement.setObject(i + 1, fields[i].get(args[0]));
}
return statement.executeUpdate();
} else if ("delete".equals(wrapper.getType())) {
for (int i = 0; i < args.length; i++) {
statement.setObject(i + 1, args[i]);
}
return statement.executeUpdate();
//args是代理对象传进的实参
} else if ("select".equals(wrapper.getType())) {
for (int i = 0; i < args.length; i++) {
statement.setObject(i + 1, args[i]);
}
ResultSet result = statement.executeQuery();
List list = new ArrayList();
while (result.next()) {
Class<?> clazz = Class.forName(wrapper.getResultType());
Object object = clazz.newInstance();
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
fields[i].set(object, result.getObject(fields[i].getName()));
}
list.add(object);
}
return list;
}
return null;
}
}
DaoWrapper:
/**
* 对应xxxMapper.xml的类 用于描述一个Dao的方法的必要条件
*/
public class DaoWrapper implements Serializable {
/**
* sql语句类型,insert|update|delete
*/
private String type;
/**
* 返回值的类型
*/
private String resultType;
/**
* 参数的类型
*/
private String paramType;
/**
* sql语句
*/
private String sql;
public DaoWrapper(String type, String resultType, String paramType, String sql) {
this.type = type;
this.resultType = resultType;
this.paramType = paramType;
this.sql = sql;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
@Override
public String toString() {
return "DaoWrapper{" +
"type='" + type + '\'' +
", resultType='" + resultType + '\'' +
", paramType='" + paramType + '\'' +
", sql='" + sql + '\'' +
'}';
}
}
数据源工厂
public class DataSourceFactory {
//不要直接在方法体内部写双引号的字符串内容 阿里规约这么写:
public static final String DATASOURCE_TYPE_HIKARI = "hikari";
public static final String DATASOURCE_TYPE_DRUID = "druid";
public static DataSource createDataSource(String type){
DataSource dataSource = null;
Properties properties = new Properties();
if(DATASOURCE_TYPE_HIKARI.equals(type)){
try {
properties.load(DataSourceFactory.class.getClassLoader().getResourceAsStream("hikari.properties"));
} catch (IOException e) {
e.printStackTrace();
}
HikariConfig hikariConfig = new HikariConfig(properties);
dataSource = new HikariDataSource(hikariConfig);
} else if (DATASOURCE_TYPE_DRUID.equals(type)){
try {
properties.load(DataSourceFactory.class.getClassLoader().getResourceAsStream("druid.properties"));
} catch (IOException e) {
e.printStackTrace();
}
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.configFromPropety(properties);
dataSource = druidDataSource;
}
return dataSource;
}
}
人承担的工作:
测试用例:
public class Test {
public static void main(String[] args) {
SessionFactory sessionFactory = new SessionFactory("mybatis-config.xml");
Session session = sessionFactory.openSession();
UserDao userDao=session.getMapper(UserDao.class); //代理
userDao.saveUser(new User(7,"al","bk"));
}
}
UserMapper.xml:
<mapper namespace="com.itnls.dao.UserDao">
<insert id="saveUser" resultType="java.lang.Integer" paramType="com.itnls.entity.User">
insert into user_nls values(?,?,?)
</insert>
<select id="findUser" resultType="com.itnls.entity.User" paramType="java.lang.Integer">
select * from user_nls where id = ?
</select>
</mapper>
UserDao:
public interface UserDao {
Integer saveUser(User user);
List<User> findUser(Integer id);
}
mybatis-config.xml:
<mybatis>
<dataSource>druid</dataSource>
<mapper>mapper/userMapper.xml</mapper>
</mybatis>