目录
1、目录结构
2、概述
- mappedstatement
记录mapper.xml的配置信息 - configuration
记录dp.properties信息,和所有的mapper.xml信息(用hashmap记录) - sqlSessionFactory
- 加载dp.properties文件,封装到configuration对象中。
- 加载mapper.xml文件,加载到mappedstatement对象中。在把mappedstatement对象封装到configuration对象中的hashmap中。
- 创建session对象。
- sqlsession
sqlsession是一个接口,他的默认实现类是DefaultSqlSession类,他的作用是:对外提供访问的api(selectone/selectlist)。将请求转发给executor。 - Executor
Executor是一个接口,他的默认的实现类是DefaultExecutor。拿到configuration类的配置信息连接数据库,执行代码。
3、实现
mappedstatement
记录mapper.xml的信息
package com.lzq.mybatis.conf;
/*
存储mypper.xml信息
*/
public class MappedStatement {
private String namespace;
private String sourceId;
private String resultType;
private String sql;
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getSourceId() {
return sourceId;
}
public void setSourceId(String sourceId) {
this.sourceId = sourceId;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}
configuration
记录db.properties信息,用HashMap<String,MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();记录mappedstatement类,key为namespace+id。
package com.lzq.mybatis.conf;
import java.util.HashMap;
public class Configuration {
private String jdbcDriver;
private String jdbcUrl;
private String userName;
private String passWord;
/*
string:namespace + id
*/
HashMap<String,MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();
public String getJdbcDriver() {
return jdbcDriver;
}
public void setJdbcDriver(String jdbcDriver) {
this.jdbcDriver = jdbcDriver;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public HashMap<String, MappedStatement> getMappedStatements() {
return mappedStatements;
}
public void setMappedStatements(HashMap<String, MappedStatement> mappedStatements) {
this.mappedStatements = mappedStatements;
}
}
SqlSessionFactory
加载dp.properties文件,封装到configuration对象中。
加载mapper.xml文件,加载到mappedstatement对象中。在把mappedstatement对象封装到configuration对象中的hashmap中。
创建session对象。
package com.lzq.mybatis.session;
import com.lzq.mybatis.conf.Configuration;
import com.lzq.mybatis.conf.MappedStatement;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;
//实例化过程中加载配置文件信息到Configuration对象中
//生产sqlSession
public class sqlSessionFactory {
private Configuration configuration = new Configuration();
public sqlSessionFactory() {
//加载db.properties配置信息到Configuration中。
loadDBInfo();
//加载mapper.xml文件信息到Configuration中。
loadMapperInfo();
}
public sqlSession openSession(){
return new DefaultSqlSession(configuration);
}
//记录mapper.xml文件的位置
public static final String MAPPER_CONFIG_LOCATION = "mappers";
//记录数据库配置文件存放位置(db.properties)
public static final String DB_CONFIG_FILE = "db.properties";
private void loadDBInfo(){
//加载db.properties
InputStream dbIn = sqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
Properties properties = new Properties();
try{
properties.load(dbIn);
}catch (Exception e){
e.printStackTrace();
}
//将db.properties文件信息读入configuration对象中
configuration.setJdbcDriver(properties.get("jdbc.driver").toString());
configuration.setJdbcUrl(properties.get("jdbc.url").toString());
configuration.setUserName(properties.get("jdbc.username").toString());
configuration.setPassWord(properties.get("jdbc.password").toString());
}
private void loadMapperInfo(){
URL resource = null;
resource = sqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
File mappers = new File(resource.getFile());
//遍历mapper.xml加载到conf文件夹
if(mappers.isDirectory()){
File[] listFiles = mappers.listFiles();
for(File file : listFiles){
loadMapperInfo(file);
}
}
}
//加载单独的一个mapper.xml
private void loadMapperInfo(File file) {
//通过SAXReader读取文件
SAXReader saxReader = new SAXReader();
//通过read方法读取一个文件转化成Document文件
Document document = null;
try{
document = saxReader.read(file);
}catch (Exception e){
e.printStackTrace();
}
//获取根节点元素,mapper
Element root = document.getRootElement();
//获取namespace元素
String namespace = root.attribute("namespace").getData().toString();
//获取select子列表元素
List<Element> selects = root.elements("select");
for(Element element : selects){
//将获取到的内容存放到MappedStatement对象中
MappedStatement mappedStatement = new MappedStatement();
String resultType = element.attribute("resultType").getData().toString();
String id = element.attribute("id").getData().toString();
String sql = element.getData().toString();
String sourceId = namespace + "." + id;
mappedStatement.setResultType(resultType);
mappedStatement.setSql(sql);
mappedStatement.setNamespace(namespace);
mappedStatement.setSourceId(sourceId);
configuration.getMappedStatements().put(sourceId,mappedStatement);
}
}
}
SqlSession
sqlsession是一个接口,他的默认实现类是DefaultSqlSession类,他的作用是:对外提供访问的api(selectone/selectlist)。将请求转发给
SqlSession
package com.lzq.mybatis.session;
import java.util.List;
public interface sqlSession {
//查询多条记录 statement == sourceid == namespace + id
public <E> List<E> selectList(String statement,Object parameters);
//查询一条记录
public <T> T selectOne(String statement,Object parameters);
//通过动态代理实现面向接口的编程模型
public <T> T getMapper(Class<T> type);
}
DefaultSqlSession
package com.lzq.mybatis.session;
import com.lzq.mybatis.binding.MapperProxy;
import com.lzq.mybatis.conf.Configuration;
import com.lzq.mybatis.conf.MappedStatement;
import com.lzq.mybatis.executor.DefaultExecutor;
import com.lzq.mybatis.executor.Executor;
import java.lang.reflect.Proxy;
import java.util.List;
//对外提供访问的api
//将请求转发给executor
public class DefaultSqlSession implements sqlSession{
private final Configuration conf;
private Executor executor;
public DefaultSqlSession(Configuration conf) {
this.conf = conf;
executor = new DefaultExecutor(conf);
}
//statement == sourceid == namespace + id
public <E> List<E> selectList(String statement,Object parameters){
MappedStatement ms = conf.getMappedStatements().get(statement);
return executor.query(ms,parameters);
}
//查询一条记录
public <T> T selectOne(String statement, Object parameters) {
List<Object> res = this.selectList(statement,parameters);
if(res == null && res.size() == 0){
return null;
}else if(res.size() == 1){
return (T) res.get(0);
}else{
throw new RuntimeException("查询有多条记录");
}
}
//通过动态代理实现面向接口的编程模型
public <T> T getMapper(Class<T> type){
MapperProxy mp = new MapperProxy(this);
return (T) Proxy.newProxyInstance(type.getClassLoader(),new Class[]{type},mp);
}
}
Executor
Executor
package com.lzq.mybatis.executor;
import com.lzq.mybatis.conf.MappedStatement;
import java.util.List;
public interface Executor {
public <E> List<E> query(MappedStatement ms, Object parameters);
}
DefaultExecutor
package com.lzq.mybatis.executor;
import com.lzq.mybatis.conf.Configuration;
import com.lzq.mybatis.conf.MappedStatement;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class DefaultExecutor implements Executor{
private Configuration conf;
public DefaultExecutor(Configuration configuration) {
this.conf = configuration;
}
public <E> List<E> query(MappedStatement ms, Object parameters) {
System.out.println(ms.getSql());
//返回结果
List<E> res = new ArrayList<E>();
try{
//加载驱动
Class.forName(conf.getJdbcDriver());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try{
//获取Connection连接,从conf中获取连接的信息。
connection = DriverManager.getConnection(conf.getJdbcUrl(),conf.getUserName(),conf.getPassWord());
//创建preparestatement信息,从mapperstatement中获取sql语句信息。
preparedStatement = connection.prepareStatement(ms.getSql());
//设置parameter
handleParameters(preparedStatement,parameters);
//执行sql获取resultSet
resultSet = preparedStatement.executeQuery();
Class domainClass = Class.forName(ms.getResultType());
while (resultSet.next()) {
//实例化要封装的实体类对象
E obj = (E) domainClass.newInstance();
//取出结果集的元信息:ResultSetMetaData
ResultSetMetaData rsmd = resultSet.getMetaData();
//取出总列数
int columnCount = rsmd.getColumnCount();
//遍历总列数
for (int i = 1; i <= columnCount; i++) {
//获取每列的名称,列名的序号是从1开始的
String columnName = rsmd.getColumnName(i);
//根据得到列名,获取每列的值
Object columnValue = resultSet.getObject(columnName);
//给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
//要求:实体类的属性和数据库表的列名保持一种
PropertyDescriptor pd = new PropertyDescriptor(columnName, domainClass);
//获取它的写入方法
Method writeMethod = pd.getWriteMethod();
//把获取的列的值,给对象赋值
writeMethod.invoke(obj, columnValue);
}
//把赋好值的对象加入到集合中
res.add(obj);
}
}catch (Exception e){
throw new RuntimeException(e);
}finally {
try{
resultSet.close();
preparedStatement.close();
connection.close();
}catch (SQLException e){
e.printStackTrace();
}
}
return res;
}
//处理parameters
public void handleParameters(PreparedStatement preparedStatement,Object parameter) throws SQLException {
if(parameter instanceof Integer){
preparedStatement.setInt(1, (Integer) parameter);
}else if(parameter instanceof Long){
preparedStatement.setLong(1, (Long) parameter);
}else if(parameter instanceof String){
preparedStatement.setString(1, (String) parameter);
}
}
}
动态代理
ibatis访问方式:
session.selectOne("com.lzq.mybatis.mapper.userMapper.selectById","1");
session.selectList("com.lzq.mybatis.mapper.userMapper.selectAll",null);
这样的访问方式参数太长了,不方便所以mybatis改成了面向接口的编程模型。采用动态代理的方式来代替以上的方式。通过传入的接口拿到类名和方法名 == namespace + id代替以上的第一个参数。
package com.lzq.mybatis.binding;
import com.lzq.mybatis.session.sqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;
public class MapperProxy implements InvocationHandler {
private sqlSession session;
public MapperProxy(sqlSession session) {
this.session = session;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果是Collection的子类,那么就调用selectList否则就调用selectOne
if(Collection.class.isAssignableFrom(method.getReturnType())){
return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(),args == null?null:args[0]);
}else{
return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),args == null?null:args[0]);
}
}
}
测试
编写User类
package com.lzq.mybatis.entity;
public class User {
private String id;
private String name;
private Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
编写Mapper接口
package com.lzq.mybatis.mapper;
import com.lzq.mybatis.entity.User;
import java.util.List;
public interface userMapper {
User selectById(String id);
List<User> selectAll();
}
编写mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.lzq.mybatis.mapper.userMapper">
<!--suppress MybatisXMapperXmlInspection -->
<select id="selectById" resultType="com.lzq.mybatis.entity.User">
select * from user where id = ?
</select>
<!--suppress MybatisXMapperXmlInspection -->
<select id="selectAll" resultType="com.lzq.mybatis.entity.User">
select * from user
</select>
</mapper>
编写db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/crm_ssm?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
编写测试类
public class test {
@Test
public void test(){
sqlSessionFactory factory = new sqlSessionFactory();
sqlSession session = factory.openSession();
System.out.println(session.toString());
//通过动态代理实现面向接口的编程模式
//session.selectOne("com.lzq.mybatis.mapper.userMapper.selectById","1");
userMapper mapper = session.getMapper(userMapper.class);
User user = mapper.selectById("2");
System.out.println(user);
//session.selectList("com.lzq.mybatis.mapper.userMapper.selectAll",null);
/*List<User> selectAll = mapper.selectAll();
for(User saleVisit : selectAll){
System.out.println(saleVisit.toString());
}*/
}
}
结果
selectone
selectlist