/**
* SessionFactory sessionFactory=new SessionFactory("mybatis-config.xml");
* 1,创建一个sessionFactory对象,用来创建session对象,需要闯入参数mybatis-config.xml
* 2,sessionFactory对象初始化时掉用loadxml(config)方法,config就是mybatis-config.xml
* 3,loadxml(config)用来对mybatis-config.xml解析,
* 获取它里面配置的数据源名字,使用DataSourceFactory创建对应的数据源。
* 获取需要加载dao.xml文件的路径,遍历路径去解析对应的dao.xml配置文件。
* 存储dao.xml的命名空间,每个方法的SQL类型,参数类型,返回值类型,参数类型。
* SessionFactory初始化配置信息完成,dao方法与dao.xml配置文件的关系建立,这些信息是属于工厂
*
* Session session= sessionFactory.openSession();
* 4,使用sessionFactory创建session,工厂的openSession()方法会将它的配置信息与session对象进行绑定。
* IUserDao userDao=session.getMapper(IUserDao.class);
* 5,session的getMapper方法使用了动态代理机制,闯入的接口IUserDao中的所有方法进行了增强,是它可以执行SQL语句
* getMapper方法中具体执行SQL语句的方法是SqlInvocationHandler类中的invoke方法
* 它从session上绑定的信息,调用响应的方法
* userDao.saveUser(new User(12,"A","B"));
* 6,进行方法的调用。
*
*/
pom.xml文件
4.0.0
SpringMVC
SpringMVC
1.0-SNAPSHOT
war
SpringMVC Maven Webapp
http://www.example.com
UTF-8
10.0.1
10.0.1
junit
junit
4.12
test
javax.servlet
servlet-api
2.5
provided
dom4j
dom4j
1.6.1
org.apache.commons
commons-lang3
3.5
org.projectlombok
lombok
1.18.18
provided
com.fasterxml.jackson.core
jackson-databind
2.8.1
cglib
cglib
3.3.0
mysql
mysql-connector-java
5.1.47
dev.tuxjsql
hikaricp-cp
2.1
com.alibaba
druid
1.2.2
org.slf4j
slf4j-nop
1.7.2
SpringMVC
maven-clean-plugin
3.1.0
maven-resources-plugin
3.0.2
maven-compiler-plugin
3.8.0
maven-surefire-plugin
2.22.1
maven-war-plugin
3.2.2
maven-install-plugin
2.5.2
maven-deploy-plugin
2.8.2
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
10.0.1
10.0.1
-parameters
UTF-8
src/main/java
**/*.xml
src/main/resources
**/*.*
1,user类和IUserDao接口:懂的都懂,不解释
package com.bruce.POJO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer uid;
private String userName;
private String password;
}
package com.bruce.dao;
import com.bruce.POJO.User;
import java.util.List;
/**
* service层掉用该接口的方法
* 该方法应该要与userMapper.xml配置文件中的sql语句一一对应,并且可以传入参数,返回查询的内容
* 因此需要有一个类对两者进行绑定
*/
public interface IUserDao {
void saveUser(User user);
List findAllUser();
User getUserById(Integer id);
}
2,Test类:测试测试是否可以操作数据库
package com.bruce;
import com.bruce.POJO.User;
import com.bruce.dao.IUserDao;
import com.mybatis.Session;
import com.mybatis.SessionFactory;
public class Test {
public static void main(String[] args) {
//根据配置文件mybatis-config.xml创建session工厂
SessionFactory sessionFactory=new SessionFactory("mybatis-config.xml");
Session session= sessionFactory.openSession();//创建session
System.out.println(session);//查看创建成功了吗
IUserDao userDao=session.getMapper(IUserDao.class);
//使用session初始化配置信息,建立dao接口与dao.xml配置文件的关系
userDao.saveUser(new User(11,"陈建江","123"));//掉用相关的方法
}
}
3,DaoWrapper类:存储xml文件中的:SQL类型,方法名,参数类型,返回值类型的一个POJO类(用于记录他们之间的联系)
package com.mybatis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
* 用来存储dao执行SQL语句的基本信息
*/
@AllArgsConstructor
@Data
@ToString
public class DaoWrapper implements Serializable {
private String type; //SQL语句的类型 insert delete select
private String resultMap;//返回值类型
private String paramType;//传入的参数类型
private String sql;//sql语句
}
4,DataSourceFactory类:生成数据源的工厂,可以根据参数名来选择实例化的数据源(hikari,Druid)
package com.mybatis;
import com.alibaba.druid.pool.DruidDataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* 数据源工厂,使用工厂设计模式,创建数据源对象
* 把数据源对象复杂的创建过程交给工厂,如果需要数据源对象时,直接向工厂拿就行了
*/
public class DataSourceFactory {
public static DataSource createDataSource(String type){
DataSource dataSource=null;
Properties properties=new Properties();//用来加载properties配置文件
if ("hikari".equals(type)){//如果闯入的参数是hikari
try {//加载hikari.properties配置文件
properties.load(DataSourceFactory.class.getClassLoader().getResourceAsStream("hikari.properties"));
} catch (IOException e) {
e.printStackTrace();
}
HikariConfig hikariConfig=new HikariConfig(properties);//创建数据源对象,需要在pom.xml中导入数据源的包
dataSource = new HikariDataSource(hikariConfig);
}else if ("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;
}
}
5,Session类:会话类,我们想要与数据库交互,需要建立会话,会话携带数据源才可以连接数据库。该类里面有事物的相关方法(开始会话,提交会话,回滚会话)
package com.mybatis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Session {
private Connection connection;//每个会话都持有一个链接
private Map> env;
//开始会话
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();
}
}
/**
* 参数是dao接口,用来根据dao接口名 来找到dao.xml文件,读取里面信息,最后把两者绑定起来
* @param clazz
* @param
* @return
*/
public T getMapper(Class clazz){
//传入dao层的接口:例如userDao(处理器中需要闯入的参数是 连接,环境集合(所有与SQL语句相关的东西))
T t = (T)Proxy.newProxyInstance(Session.class.getClassLoader(), new Class[]{clazz}, new SqlInvocationHandler(connection,env.get(clazz.getName())));
return t;
}
}
6,SessionFactory类:创建session实例对象的工厂(为什么要使用工厂来创建,因为可以把session复杂的创建过程影藏,每当我们使用session对象时,只需要调用它里面的创建方法就可以创建了)
package com.mybatis;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
/**
* 每次要打开一个会话,我都去取一个数据源放到session中
* session对象创建的时候,需要实现将dao接口和dao.xml文件中的基本信息绑定存储在Map中
* 因此session对象初始化的过程比较复杂,使用工厂来创建session,实现session对象的创建和使用的分离
*/
public class SessionFactory {
private static DataSource dataSource;
private static Map> env=new HashMap<>(8);//存储配置文件信息的环境
// nameSpace 方法名 参数类型,返回值类型,SQL语句的集合
public SessionFactory(String config){//传入的参数是一个配置文件名,例如:mybatis-config.xml
loadxml(config);//初始化,参数是mybatis-config.xml配置文件的名字
}
//打开一个会话,绑定一个数据源
public Session openSession(){
Connection connection=null;
try {
connection=dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return new Session(connection,env);//会话绑定一个数据源
}
//解析mybatis-config.xml文件,初始化资源
public void loadxml(String config){
SAXReader reader=new SAXReader();//解析xml文件的对象
//得到mybatis-config.xml文件的二进制流
InputStream source= SessionFactory.class.getClassLoader().getResourceAsStream(config);
Document configDom = null;
if (source!=null){
System.out.println("xml流文件不为空");
try {
configDom = reader.read(source);//xml解析对象,解析.xml文件的二进制流
Element rootElement = configDom.getRootElement();//得到mybatis-config.xml文件根节点,
String dataSourceName = rootElement.element("dataSource").getTextTrim();//得到数据源的名字
dataSource = DataSourceFactory.createDataSource(dataSourceName);//使用工厂创建数据源
List mappers = rootElement.elements("mapper");//获得mapper标签集合
List mapperPaths=new ArrayList<>();
for (Object element: mappers){//遍历标签得到里面的dao.xml文件的映射路径
Element mapper= (Element)element;
mapperPaths.add(mapper.getTextTrim());//存储路径
}
for (String mapperPath:mapperPaths){//遍历所有的mapperDao.xml对它们的数据进行封装。
Map wrapper=new HashMap<>(8);
Document document = reader.read(Session.class.getClassLoader().getResourceAsStream(mapperPath));
Element root = document.getRootElement();//获取根节点mapper
String namespace = root.attribute("namespace").getValue();//得到命名空间
Iterator iterator = root.elementIterator();
while (iterator.hasNext()){//通过迭代器获取mapper标签下的其他标签,把它们的相关数据封装到我DaoWrapper中
Element el = (Element)iterator.next();//获取每个标签
String type = el.getName();//获取到标签的名字,也就是SQL语句的类型
String id = el.attribute("id").getValue();//获取id值,也就是方法名
String resultMap="";
String paramType ="";
//如果参数
if (el.attribute("resultType")!=null){
resultMap= el.attribute("resultType").getValue();//获取返回值类型
}
if (el.attribute("parameterType")!=null){
paramType=el.attribute("parameterType").getValue();//获取返回值类型
}
String sql = el.getTextTrim();//得到SQL语句的字符串
wrapper.put(id, new DaoWrapper(type,resultMap,paramType,sql));
}
env.put(namespace,wrapper);//最终封装好的环境数据
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
7,SQLInnovationHandler类:为了使用jdk动态代理创建的类,负责对具体SQL语句执行
package com.mybatis;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class SqlInvocationHandler implements InvocationHandler {
private Connection connection;
private Map env;
public SqlInvocationHandler(Connection connection,Map env){
this.connection=connection;
this.env=env;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();//得到dao层要调用的方法名
DaoWrapper daoWrapper = env.get(name);//根据方法名得到它的环境信息
PreparedStatement statement = connection.prepareStatement(daoWrapper.getSql());//根据sql得到预编译语句
if ("insert".equals(daoWrapper.getType())){
//假设传入一个参数
String paramType = daoWrapper.getParamType();//得到参数类型
Class> clazz = args[0].getClass();
Field[] fields = clazz.getDeclaredFields();
for(int i=0;i clazz = Class.forName(daoWrapper.getResultMap());//通过反射机制,根据返回值类型创建实例
Object object = clazz.newInstance();
Field[] fields = clazz.getDeclaredFields();//通过反射机制,该类型的所有属性
for (int i=0;i
8,userMapper.xml:编写具体的SQL语句
insert into t_user(uid,userName,password) values (?,?,?);
10,druid.properties:druid数据源连接数据库的信息
druid.driverClassName=com.mysql.jdbc.Driver
druid.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8
druid.username=root
druid.password=111
11,hikari.properties:hikari连接数据库的信息
jdbcUrl=jdbc:mysql://localhost:3306/test
username=test
password=123456
maximumPoolSize=30
minimumIdle=5
connectionTestQuery=SELECT 1
autoCommit=true
dataSource.cachePrepStmts=true
dataSource.prepStmtCacheSize=250
dataSource.prepStmtCacheSqlLimit=2048
dataSource.useServerPrepStmts=true
dataSource.useLocalSessionState=true
dataSource.useLocalTransactionState=true
dataSource.rewriteBatchedStatements=true
dataSource.cacheResultSetMetadata=true
dataSource.cacheServerConfiguration=true
dataSource.elideSetAutoCommits=true
dataSource.maintainTimeStats=false
12,mybatis-config.xml:存储数据源的名字,和userDao.xml文件的路径(用于对dao.xml文件的管理)
druid
mapper/userMapper.xml
mapper/studentMapper.xml
源码:里面有SpringMVC和mybatis手写框架
链接:https://pan.baidu.com/s/1iGEUs2l0v6jSDtoPEI-Kwg
提取码:uxet
复制这段内容后打开百度网盘手机App,操作更方便哦
视频:https://www.bilibili.com/video/BV1NT4y1P73V?from=search&seid=9842311074072221898
我已经实现SpringMVC框架,和mybatis框架的基本功能的编写,并且实例化对象也是放在一个类似于spring容器中的,使用注解的方式实现对象的注入
目前service层,controller层对象注入完成,还需要实现dao接口的注入。由于注入是在
目标:
问题:Method threw 'java.lang.NullPointerException' exception. Cannot evaluate com.sun.proxy.$Proxy8.toString()