思路:1.通过xml文件记录信息,
2.通过Resources类读取xml文件,
3.通过构建者(SqlSessionFactoryBuilder)调用XmlConifgBuilder解析xml文件,将获取的信息封装在Configuration中和Mapper中(Configuration中包含存储Mapper的容器mappers),然后创建工厂的实体类(DefauSqlSessionFactory),
4.工厂传递Configuration给创建的SqlSession的实体类(DefaSqlSesison)
5.SqlSession实体类通过方法getMapper(参数:dao接口类的字节码),返回匿名内部类,该匿名内部类包括MapperPrxoy由于该代理类需要Configuraiton中的mappers和connection,于是SqlSession实体类创建了Connection对象,并通过工具类DatasourceUtil获取连接,返回给代理类
6.MapperProxy是dao接口类的代理类,于是通过当前执行的方法的方法名,和该方法所在的类名,连接成key,从mappers中获取对应的Mapper,并用Mapper中的sql语句和返回值类型,调用Executor类的方法,实现查询。
以下先从环境入手:
- maven部分:
< dependencies >
< dependency >
< groupId >log4j< /groupId>
< artifactId >log4j< /artifactId >
< version>1.2.12< /version>
< /dependency>
< dependency>
< groupId>junit< /groupId>
< artifactId>junit< /artifactId>
< version>4.11</ version>
< scope>test</ scope>
</ dependency>
<!-- 导入dom4j解析xml工具的依赖-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
< !-- < dependency>-->
< !-- < groupId >org.mybatis</ groupId>-->
< !-- < artifactId>mybatis</ artifactId>-->
< !-- < version>${mybatis.version}</ version>-->
< !-- < /dependency>-->
</ dependencies>
以上为pom.xml文件内容,注意:注释掉mybatis的依赖
2.以下为主配置文件部分:
< configuration>
<!-- 配置环境-->
<environments default="mysql">
<!-- 配置mysql-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!-- 配置映射路径-->
<mappers>
<!--<!– 记得使用文件夹格式的路径–>-->
<mapper resource="huang/dao/IUserDao.xml"></mapper>
<!-- 直接指向接口类,使用注解进行配置-->
<!-- <mapper class="huang.dao.IUserDao"/>-->
</mappers>
</configuration>
3.映射文件部分:
< mapper namespace= "huang.dao.IUserDao" >
< select id="findAll" resultType="huang.domain.User">
select * from usertable
</ select>
</mapper>
4.Resources类:
import java.io.InputStream;
//1.提供获取xml的方法
public class Resources {
public static InputStream getResourceAsStream(String mybatis){
InputStream resource = Resources.class.getClassLoader().getResourceAsStream(mybatis);
return resource;
}
}
5.SqlSessionFactoryBuilder类
import huang.mybatis.config.Configuration;
import huang.mybatis.session.imp.DefaultSqlSessionFactory;
import huang.mybatis.utils.XMLConfigBuilder;
import java.io.InputStream;
public class SqlSessionFactoryBuilder {
//1.提供建立工厂的方法
public SqlSessionFactory build(InputStream inputStream) {
Configuration configuration = XMLConfigBuilder.loadConfiguration(inputStream);
return new DefaultSqlSessionFactory(configuration);
}
}
6.SqlSessionFactory类和其实现类
public interface SqlSessionFactory {
//1.提供给出session方法
SqlSession openSession();
}
import huang.mybatis.config.Configuration;
import huang.mybatis.session.SqlSession;
import huang.mybatis.session.SqlSessionFactory;
//实现了工厂类,那么就要提供一个可以操作数据库的类
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//1.提供可以操作数据库的类
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
public DefaultSqlSessionFactory() {
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
7.SqlSession类和其实现类:
public interface SqlSession {
//1.提供返回Mapper方法和关闭方法
<t> t getMapper(Class<t> daoClass);
//关闭方法
void close();
}
import huang.mybatis.config.Configuration;
import huang.mybatis.proxy.MapperProxy;
import huang.mybatis.session.SqlSession;
import huang.mybatis.utils.DatasourceUtil;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
public class DefaultSqlSession implements SqlSession {
//1.用于创建代理对象
private Configuration config;
private Connection connection;
public DefaultSqlSession(Configuration config) {
this.config = config;
connection = DatasourceUtil.getConnection(config);
}
@Override
public <t> t getMapper(Class<t> daoClass) {
//1.创建代理对象
// Proxy.newProxyInstance(daoClass.getClassLoader(), new Class[]{daoClass}, new MapperProxy(config.getMappers(),connection));
return (t) Proxy.newProxyInstance(daoClass.getClassLoader(),
new Class[]{daoClass},new MapperProxy(config.getMappers(),connection));
}
//2.用于释放资源
@Override
public void close() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
8.Configuration类,注意:要new一个HashMap,否则直接使用会报空指针异常,其次,要使用putAll方法,否则在XmlConfigBuilder中:
for(Element mapperElement : mapperElements){
//判断mapperElement使用的是哪个属性
Attribute attribute = mapperElement.attribute("resource");
if(attribute != null){
System.out.println("使用的是XML");
//表示有resource属性,用的是XML
//取出属性的值
String mapperPath = attribute.getValue();//获取属性的值"com/itheima/dao/IUserDao.xml"
//把映射配置文件的内容获取出来,封装成一个map
Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
//给configuration中的mappers赋值
cfg.setMappers(mappers);
}
中,就会出现setMappers调用多次,覆盖上次的情况出现,所以采用追加的方式加入
import java.util.HashMap;
import java.util.Map;
public class Configuration {
private String username;
private String url;
private String password;
private String driver;
private Map<String, Mapper> mappers = new HashMap<String, Mapper>();//如果不适用new出来的方式,则会空指针异常
public Map<String, Mapper> getMappers() {
return mappers;
}
public void setMappers(Map<String, Mapper> mappers) {
// this.mappers = mappers;
//由于xmlConfigBuilder类中,通过for循环的方式,多次调用setMappers方法
//若直接等于,那么就是每次set都用新的代替旧的,旧的就消失了
this.mappers.putAll(mappers);//使用追加的方式
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
}
9.Mapper类:
public class Mapper {
//1.封装了sql语句和全限定类名的类
private String queryString;//sql语句
private String resultType;//全限定类名
public String getQueryString() {
return queryString;
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
@Override
public String toString() {
return "Mapper{" +
"queryString='" + queryString + '\'' +
", resultType='" + resultType + '\'' +
'}';
}
}
10.MapperProxy类:
import huang.mybatis.config.Mapper;
import huang.mybatis.utils.Executor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Map;
public class MapperProxy implements InvocationHandler {
private Map<String, Mapper> mapppers;
private Connection con;
public MapperProxy(Map<String, Mapper> mapppers, Connection con) {
this.mapppers = mapppers;
this.con = con;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.获取当前方法名
String name = method.getName();
//2.获取方法所在类全限定类名
String className = method.getDeclaringClass().getName();
//3.组合成KEY
String key = className + "." + name;
//4.判断是否又mapper
Mapper mapper = mapppers.get(key);
if (mapper == null) {
throw new IllegalArgumentException("传入参数错误");
}
//5.利用工具类执行查询
return new Executor().selectList(mapper,con);//使用匿名内部类
}
}
11.DatasourceUtil类:
public class DatasourceUtil {
//1.用于获取一个连接
public static Connection getConnection(Configuration configuration) {
Connection connection=null;
try {
Class.forName(configuration.getDriver());
connection = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
12.关于各个类的功能:
1.配置文件xml:填写连接信息和映射文件所在位置
2.dao接口的映射:提供接口的实现方法,全类名,返回值类型
3.Resouces类:简单封装读取文件的方式。
4.xmlConifgBuilder类:用于解析xml文件,获取其中的信息
5.SqlSessionFactoryBuilder类:构建者类,调用xmlConfigBuilder类获取信息创建Configuration对象,返回工厂的实现类(将configuration传递给工厂)
6.Configuration类:封装连接信息,和Mapper类,
7.Mapper类:一个mapper对应一个dao类,封装sql信息,和返回值的全限定类名
8.Executory类:进行数据库查询,返回list集合,需要传参(mapper,connection)
9.DefaSqlSessionFactory类:将传过来的configuration传递给工厂内创建的DefaultSqlSession对象
10.DefaultSqlSession类:提供getMapper方法,返回匿名内部代理类,在此代理类中,创建了MapperProxy类;创建了一个连接
11.MapperProxy类:是代理类;包含map<String,Mapper>成员和connection成员,从session中获取;通过当前方法Method获取当前执行的方法名,和该方法所在类的全类名;然后从map中获取对应的value(mapper对象),
12.DatasourceUtil类:通过configuration对象,创建对象,返回对象
13.从信息流向的角度来看:
主要有:连接信息,查询和返回信息,含查询方法的类信息。
1.连接信息,记录在xml中,通过Resoureces方法,获取文件流,通过XmlCoinfigBuilder解析文件,封装在Configuration类中,最终在sqlSession对象中调用datasourceUitl类中解析连接信息,创建连接
2.查询和返回信息,记录在映射路径下的xml文件中,通过XmlConfigBuilder解析该信息,封装在Configuraion中的mappers容器,作为一个Mapper类对象中,最终传递给MapperProxy代理类,调用Executor中的方法,提供查询和返回信息类。
3.含查询方法的类信息,记录在映射路径下的xml文件中,XmlConfigBuilder不解析该信息,而是在session创建mapper对象的过程中,传入类.class文件,(session.getMapper(IUserDao.class));最后,在MapperProxy代理类中,通过现在执行的方法,进行获取类信息.
14.从类的流向角度来看:
Configuration承载着大部分信息,所以从构建者(SqlSessionFactoryBuilder)到MapperProxy,一直传递。Connection只创建了一个,由SqlSession创建,传递给MapperProxy。
15.最后测试部分如下:
public class MybatisTest {
public static void main(String[] args) throws IOException {
//1.读取xml文件
InputStream in = Resources.getResourceAsStream("mybatisConfig.xml");/*Resources中调用类.class.getClassLoader.
getResourcesAsStream(String mybatis)方法读取xml文件*/
//2.通过构建者模式创建工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();/*构建者通过文件流利用XMLConfigBuilder创建配置类的对象;
配置类封装了连接信息,和mapper对象;mapper对象封装了查询语句和返回值类型*/
SqlSessionFactory factory = builder.build(in);/*构建者通过配置类的对象,创建工厂的实现类。*/
//3.通过工厂获取session
SqlSession session = factory.openSession();/*将配置类传递给实现了SqlSession接口的实现类*/
//4.通过session获取mapper
IUserDao mapper = session.getMapper(IUserDao.class);/*session创建了dao接口的代理类,创建连接对象,传递给代理类mapper和
连接对象;*/
//5.执行dao方法
List<User> all = mapper.findAll();/*利用mapper中的成员变量从mappers中读取sql和返回值类,利用Executor().selectList(mapper,con)
执行方法,返回list集合*/
for (User user : all) {
System.out.println(user);
}
//6.释放资源
in.close();
session.close();
}
}