自定义Mybatis框架
使用:
idea
apache-maven-3.3.9
jdk1.8.0_162
数据库mybatis1
数据表:user
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
创建maven工程并引入坐标:
例如:创建mybatis_frame的maven工程,并引入依赖
dom4j jaxen 解析xml配置文件
<dependencies> <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> </dependencies>
自定义框架原理:
项目结构
1.编写配置文件:
SqlMapConfig.xml
UserMapper.xml
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis1" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <mapper resource="com/review/mapper/UserMapper.xml"></mapper> </mappers> </configuration> UserMapper.xml <?xml version="1.0" encoding="utf-8" ?> <mapper namespace="com.review.mapper.UserMapper"> <select id="findAll" resultType="com.review.pojo.User"> select * from user </select> </mapper>
2 编写配置文件对应的实体类:
Confinguration
public class Configuration { private String driverClass; private String url; private String username; private String password; private Map<String, Mapper> mappers = new HashMap<>(); public String getDriverClass() { return driverClass; } public void setDriverClass(String driverClass) { this.driverClass = driverClass; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } 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 Map<String, Mapper> getMappers() { return mappers; } public void setMappers(Map<String, Mapper> mappers) { this.mappers = mappers; } }
Mapper
public class Mapper { private String sql;//sql语句 public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } private String resultType;//返回值类型 }
3编写SqlSession接口及实现类SqlSessionImpl,Executor执行器
SqlSession接口:对数据库进行操作的方法
SqlSessionImpl类:对接口中的方法进行重写,在构造中创建executor对象,并对configuration赋值
Executor: 用于实现 SQL 语句的执行,主要是调用 JDBC 来实现 SQL 语句的执行
SqlSession 接口:
public interface SqlSession { //执行 public <T> List<T> selectList(String mapperId); //关闭资源 public void close(); }
SqlSessionImpl类:
public class SqlSessionImpl implements SqlSession { private Configuration cfg; private Executor executor; public SqlSessionImpl(Configuration configuration) { this.cfg = configuration; this.executor = new Executor(cfg); } public <T> List<T> selectList(String mapperId) { Mapper mapper = cfg.getMappers().get(mapperId); String sql = mapper.getSql(); String returnType = mapper.getResultType(); return executor.executorQuery(sql, returnType); } public void close() { executor.release(); } }
Executor类:用于实现 SQL 语句的执行,主要是调用 JDBC 来实现 SQL 语句的执行
public class Executor { private Configuration configuration; public Executor(Configuration configuration) { this.configuration = configuration; } private Connection conn = null; private PreparedStatement pst = null; private ResultSet rs = null; public <T> List<T> executorQuery(String sql, String returnType) { List<T> list = new ArrayList<T>(); //连接数据库,执行sql语句 try { //注册驱动 Class.forName(configuration.getDriverClass()); //获得链接 conn = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword()); //获得执行者对象 pst = conn.prepareStatement(sql); //获得结果集 rs = pst.executeQuery(); //获得元数据 ResultSetMetaData metaData = rs.getMetaData(); //字段数 int columnCount = metaData.getColumnCount(); //存放字段 List<String> columnNames = new ArrayList<String>(); //遍历字段数 for (int i = 1; i <= columnCount; i++) { //获得对应的字段名 String columnName = metaData.getColumnName(i); //放入到list集合 columnNames.add(columnName); } //获得字节码文件 Class obj = Class.forName(returnType); //解析结果集 while (rs.next()) { //创建对象 相当于 new User(); Object o = obj.newInstance(); //获得User实体类中的所有方法 Method[] methods = obj.getMethods(); for (Method method : methods) { //遍历字段 for (String columnName : columnNames) { if (method.getName().equalsIgnoreCase("set" + columnName)) { try { method.invoke(o,rs.getObject(columnName)); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } //放到对象集合中 list.add((T) o); } } catch (Exception e) { e.printStackTrace(); }/*finally { relese(); }*/ return list; } public void release() { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pst != null) { try { pst.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
4 编写SqlSessionFactory:创建SqlSession对象
构造方法中传入InputStream对象
提供返回值SqlSession 的openSession方法
加载配置文件
public class SqlSessionFactory { private InputStream inputStream; //需要传入一个流对象 public SqlSessionFactory(InputStream inputStream) { this.inputStream = inputStream; } //获取SqlSessiom 对象 public SqlSession openSession() { Configuration configuration = new Configuration(); loadConfiguration(configuration); return new SqlSessionImpl(configuration); } //读取配置文件 private void loadConfiguration(Configuration cfg){ SAXReader saxReader = new SAXReader(); Document doc = null; Element rootElement = null; try { doc = saxReader.read(inputStream); rootElement = doc.getRootElement(); List<Element> list = rootElement.selectNodes("//property"); for (Element e : list) { //e.attributeValue(name) :获取指定的属性名的属性值 //如果取出的name的属性值是driver,name就可以封装大搜Configuration中的driverClass属性中 if(e.attributeValue("name").equalsIgnoreCase("driver")){ cfg.setDriverClass(e.attributeValue("value")); } if(e.attributeValue("name").equalsIgnoreCase("url")){ cfg.setUrl(e.attributeValue("value")); } if(e.attributeValue("name").equalsIgnoreCase("username")){ cfg.setUsername(e.attributeValue("value")); } if(e.attributeValue("name").equalsIgnoreCase("password")){ cfg.setPassword(e.attributeValue("value")); } } } catch (Exception e) { e.printStackTrace(); } //加载另一个配置文件 Element mappers = rootElement.element("mappers"); List<Element> mapperList = mappers.elements(); for (Element e : mapperList) { String mapperPath = e.attributeValue("resource"); loadMapperConfiguration(mapperPath, cfg); } } private void loadMapperConfiguration(String mapperPath, Configuration cfg) { InputStream inputStream = SqlSessionFactory.class.getClassLoader().getResourceAsStream(mapperPath); SAXReader saxReader = new SAXReader(); try { Document doc = saxReader.read(inputStream); Element rootElement = doc.getRootElement(); Map<String, Mapper> mapperMap = cfg.getMappers(); String key = null; String namespace = rootElement.attributeValue("namespace"); //获取根节点下所有的子节点 List<Element> elements = rootElement.elements(); for (Element element : elements) { String id = element.attributeValue("id"); //com.itheima.mapper.UserDao.findAll key = namespace +"."+ id; // value:存储Mapper对象,返回值类型和sql语句 Mapper mapper = new Mapper(); //获取返回值类型 String resultType = element.attributeValue("resultType"); mapper.setResultType(resultType); //获取sql语句:getTextTrim{获取节点中的文本且去除两侧空白 String sql = element.getTextTrim(); mapper.setSql(sql); mapperMap.put(key,mapper); } } catch (DocumentException e) { e.printStackTrace(); } } }
5 编写SqlSessionFactoryBuilder
三个重载的builder方法:无参,String path,InputStream is;
public class SqlSessionFactoryBuilder { public SqlSessionFactory builder(InputStream inputStream){ return new SqlSessionFactory(inputStream); } public SqlSessionFactory builder(String path){ InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path); return new SqlSessionFactory(inputStream); } public SqlSessionFactory builder(){ String path = "SqlMapConfig.xml"; InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path); return new SqlSessionFactory(inputStream); } }
把自定义的mybatis打成jar:install
测试自定义框架:
创建maven测试工程:
引入依赖:
打成jar文件的自定义框架
mysql驱动
junit测试
<dependencies> <dependency> <groupId>com.review</groupId> <artifactId>mybatis_frame</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
引入配置文件:自定义mybatis一样,将resource中的文件复制一份
创建User实体类:
public class User { private Integer id; private String name; private int age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类:
public class TestMybatis { SqlSessionFactory sessionFactory = null; @Before public void init(){ InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("SqlMapConfig.xml"); sessionFactory = new SqlSessionFactoryBuilder().builder(inputStream); } @Test public void FindAll(){ SqlSession sqlSession = sessionFactory.openSession(); List<User> userList = sqlSession.selectList("com.review.mapper.UserMapper.findAll"); for (User user : userList) { System.out.println(user.getName()); } sqlSession.close(); } }