自己动手实现 SpringIOC 容器、JPA

版权声明:有问题欢迎留言,转载请声明出处 https://blog.csdn.net/larger5/article/details/82633806

一、前言

最近在编写一个自己的框架,想借此机会,通过自己的框架使用效果,检测自己对 Spring、JPA 等主流框架的源码的理解是否正确,而不是简单造轮子。
写了几天,有了点雏形,源码已经放到 GitHub 上了:
https://github.com/larger5/simpleSpring/tree/master/src/main/java/core/Dao
本文分享模拟 SringIOC、JPA 底层设计的一种简单的思路,有如下几个要点:

  • 实现 IOC 容器
    • 使用 dom4j 轻松读取 xml 中的配置信息
    • 使用工厂模式构建 IOC 容器
    • 使用反射实现从 IOC 容器中获取 bean
  • 实现 JPA 操作数据
    • 使用反射构成数据库关系映射
    • 使用 IOC 容器分离数据源配置到 xml 文件中
    • 使用 IOC 注册 Jpa bean

框架的实现使用了 lombok、dom4j,其实是可以使用原生代替代的,但是徒增逻辑
lombok 通过 @Data 等省略 setter、getter、toString 等。
dom4j 通过 xpath 语法清晰获取 xml 中信息。

二、代码

这里写图片描述

一切尽在代码的注释中
建议通过网页右边的目录来查看本文

1、注解

用来标识一些字段,其实也可以不用,因为 Hibernate 和 sun 公司已经制定了一套 Java Persistence API,位于 javax.persistence.*,如:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

为了使框架的一切都在自己的掌控之中,笔者还是自己编写几个注解。

package core.Dao.tableAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE}) // 类
@Retention(RetentionPolicy.RUNTIME) // 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
public @interface Table {
    String value();
}
package core.Dao.tableAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD}) // 作用域:属性
@Retention(RetentionPolicy.RUNTIME) // 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
public @interface Id {
}
package core.Dao.tableAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD}) // 作用域:属性
@Retention(RetentionPolicy.RUNTIME) // 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
public @interface Column {
    String value();
}

2、生成 sql 语句

queryByEntity
①先判断字段是否有,@Column@Id@Table
②若有,判断字段的值是否为空
③若非空,作为查询的条件
④拼接 sql

package core.Dao.BaseCrud;

import core.Dao.tableAnnotation.Column;
import core.Dao.tableAnnotation.Id;
import core.Dao.tableAnnotation.Table;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BaseSql {

    public String queryByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("select * from ").append(tableName).append(" where 1=1");
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // 去掉 @Column 但为 null 的属性
            if (fieldValue == null) {
                continue;
            }
            // 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
            sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
        }
        return sql.toString();
    }

    public String countByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("select count(*) from ").append(tableName).append(" where 1=1");
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // 去掉 @Column 但为 null 的属性
            if (fieldValue == null) {
                continue;
            }
            // 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
            sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
        }
        return sql.toString();
    }

    public String queryByEntityAndPage(Object entity, Integer page, Integer size) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("select * from ").append(tableName).append(" where 1=1");
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // 去掉 @Column 但为 null 的属性
            if (fieldValue == null) {
                continue;
            }
            // 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
            sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
        }
        // 分页
        sql.append(" limit ").append((page-1)*size).append(",").append(size);
        return sql.toString();
    }

    public String deleteByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("delete from ").append(tableName).append(" where 1=1");
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // 去掉 @Column 但为 null 的属性
            if (fieldValue == null) {
                continue;
            }
            // 拼装 sql 注意 ' 不能省略,否则 MySQLSyntaxErrorException:Unknown column
            sql.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
        }
        return sql.toString();
    }

    public String insertByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        StringBuilder insertField = new StringBuilder();
        StringBuilder insertValue = new StringBuilder();

        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("insert into ").append(tableName);
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // 去掉 @Column 但为 null 的属性
            if (fieldValue == null) {
                continue;
            }
            insertField.append(columnName).append(",");
            insertValue.append("'").append(fieldValue).append("'").append(",");
        }
        sql.append("(")
                .append(insertField.substring(0, insertField.length() - 1))
                .append(")")
                .append(" values(")
                .append(insertValue.substring(0, insertValue.length() - 1))
                .append(")");
        return sql.toString();
    }

    public String updateByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        StringBuilder condition = new StringBuilder();

        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("update ").append(tableName);
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        // sql 拼接中是否已经有 set 了
        Boolean hasSetString = false;
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // 去掉 @Column 但为 null 的属性
            if (fieldValue == null) {
                continue;
            }
            // id为条件(缺陷:目前只能有一个主键)
            if (field.isAnnotationPresent(Id.class)) {
                condition.append(" where ").append(columnName).append("=").append("'").append(fieldValue).append("'");
                continue;
            }
            // sql 中只出现一个 set
            if (!hasSetString) {
                sql.append(" set ");
                hasSetString = true;
            }
            sql.append(columnName).append("=").append("'").append(fieldValue).append("'").append(",");
        }
        return sql.substring(0, sql.length() - 1) + condition;
    }

    // 想去除记录中的部分字段时调用
    public String updateByEntityWithAllColumn(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        StringBuilder condition = new StringBuilder();

        // 获取 class
        Class c = entity.getClass();
        // 该类是否为 @Table 旗下的
        boolean annotationPresent = c.isAnnotationPresent(Table.class);
        if (!annotationPresent) {
            return null;
        }
        // 获取 @Table 中的信息
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        // 1=1 防止下面的 sql 拼装 and 开头报错
        sql.append("update ").append(tableName);
        // 遍历所有字段
        Field[] fields = c.getDeclaredFields();
        // sql 拼接中是否已经有 set 了
        Boolean hasSetString = false;
        for (Field field : fields) {
            // 该属性是否为 @Column 旗下的
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            // 获取 @Column 中的信息
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            // 获取方法名
            String fieldName = field.getName();
            // getXxx 格式,即属性第一个字母大写
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            // 获取 getter
            Method getMethod = c.getMethod(getMethodName); // sql 拼接错就报 NoSuchMethodException
            // 执行 getter,fieldValue 可能为 Integer、String 类型,还是 Object 稳点
            Object fieldValue = getMethod.invoke(entity);
            // id为条件(缺陷:目前只能有一个主键)
            if (field.isAnnotationPresent(Id.class)) {
                condition.append(" where ").append(columnName).append("=").append("'").append(fieldValue).append("'");
                continue;
            }
            // sql 中只出现一个 set
            if (!hasSetString) {
                sql.append(" set ");
                hasSetString = true;
            }
            // 允许置空
            if (fieldValue == null) {
                sql.append(columnName).append("=").append(fieldValue).append(",");//单引号的作用就在此体现了,其起到屏蔽关键字的作用
                continue;
            }
            sql.append(columnName).append("=").append("'").append(fieldValue).append("'").append(",");
        }
        return sql.substring(0, sql.length() - 1) + condition;
    }

}

3、执行 sql 语句

下面就简单了,都是 JDBCsql 已经明确了

package core.Dao.BaseCrud;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 优点:全是原生代码,出错时一切尽在掌握之内
 * 缺点:
 * ① 即使是 VO 对象也要 @Column、@Table、@Id,显然是不符合现实的,特别是 @Table 是不存在的
 * ② 只能单表 CRUD
 */
@Data // getter、setter、toString
@NoArgsConstructor // 无参构造函数
public class BaseJdbc {

    // 数据库驱动
    private String driver;
    // 数据库 url
    private String url;
    // 数据库库 用户名
    private String user;
    // 数据库 密码
    private String password;
    // 拼接 sql
    private BaseSql baseSql=new BaseSql();

    public List<Map<String, Object>> queryByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException, ClassNotFoundException {
        String sql = baseSql.queryByEntity(entity);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        ResultSetMetaData rsmd = ps.getMetaData();

        // 取得结果集列数
        int columnCount = rsmd.getColumnCount();

        // 构造泛型结果集
        List<Map<String, Object>> datas = new ArrayList<Map<String, Object>>();
        Map<String, Object> data = null;
        // 循环结果集
        while (rs.next()) {
            data = new HashMap<String, Object>();
            // 每循环一条将列名和列值存入Map
            for (int i = 1; i <= columnCount; i++) {
                data.put(rsmd.getColumnLabel(i), rs.getObject(rsmd
                        .getColumnLabel(i)));
            }
            // 将整条数据的Map存入到List中
            datas.add(data);
        }
        close(conn);
        return datas;
    }

    public List<Map<String, Object>> queryByEntityAndPage(Object entity,Integer page,Integer size) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException, ClassNotFoundException {
        String sql = baseSql.queryByEntityAndPage(entity,page,size);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        ResultSetMetaData rsmd = ps.getMetaData();

        // 取得结果集列数
        int columnCount = rsmd.getColumnCount();

        // 构造泛型结果集
        List<Map<String, Object>> datas = new ArrayList<Map<String, Object>>();
        Map<String, Object> data = null;
        // 循环结果集
        while (rs.next()) {
            data = new HashMap<String, Object>();
            // 每循环一条将列名和列值存入Map
            for (int i = 1; i <= columnCount; i++) {
                data.put(rsmd.getColumnLabel(i), rs.getObject(rsmd
                        .getColumnLabel(i)));
            }
            // 将整条数据的Map存入到List中
            datas.add(data);
        }
        close(conn);
        return datas;
    }

    public Integer countByEntity(Object entity) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException, ClassNotFoundException {
        String sql = baseSql.countByEntity(entity);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        Integer count=0;
        if(rs.next()) {
            count=rs.getInt(1);
        }
        close(conn);
        return count;
    }

    public Boolean deleteByEntity(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
        String sql = baseSql.deleteByEntity(entity);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        int rs = ps.executeUpdate();
        close(conn);
        if (rs < 0) {
            return false;
        }
        return true;
    }

    public Boolean insertByEntity(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
        String sql = baseSql.insertByEntity(entity);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        int columnCount = ps.executeUpdate();
        if (columnCount < 1) {
            return false;
        }
        return true;
    }

    public Boolean updateByEntity(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
        String sql = baseSql.updateByEntity(entity);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        int columnCount = ps.executeUpdate();
        close(conn);
        if (columnCount < 1) {
            return false;
        }
        return true;
    }

    public Boolean updateByEntityWithAllColumn(Object entity) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
        String sql = baseSql.updateByEntityWithAllColumn(entity);
        System.out.println(sql);
        Connection conn = getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        int columnCount = ps.executeUpdate();
        close(conn);
        if (columnCount < 1) {
            return false;
        }
        return true;
    }

    /**
     * 连接
     *
     * @return
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public Connection getConnection() throws ClassNotFoundException, SQLException {
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }

    /**
     * 断开
     *
     * @param conn
     * @throws SQLException
     */
    public void close(Connection conn) throws SQLException {
        if (conn!=null){
            conn.close();
        }
    }

}

4、构建工厂

关键:① id(标识)、② class(反射出实例)

package core.factory;

import core.Dao.BaseCrud.BaseJdbc;
import core.Dao.ComplexCrud.ComplexJdbc;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

@Data // getter、setter、toString
@AllArgsConstructor // 有参构造函数
@NoArgsConstructor // 无参构造函数
public class XmlBeanFactory {

    private String xmlPath;

    public Object getBean(String bean) throws Exception {

        SAXReader saxReader = new SAXReader();
        // 在项目中相对的是以项目名为根路径
        Document document = saxReader.read(xmlPath);
        Element firstBean = (Element) document.selectSingleNode("//bean[@id='"+bean+"']");
        // 获取类路径
        String className = (String) firstBean.attribute("class").getData();
        // 反射得类实例
        return Class.forName(className).newInstance();
    }

    public BaseJdbc getBaseJdbc()  {

        SAXReader saxReader = new SAXReader();
        // 在项目中相对的是以项目名为根路径
        Document document = null;
        try {
            document = saxReader.read(xmlPath);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        Element driver = (Element) document.selectSingleNode("//jdbc/property[@name='driver']");
        Element url = (Element) document.selectSingleNode("//jdbc/property[@name='url']");
        Element username = (Element) document.selectSingleNode("//jdbc/property[@name='username']");
        Element password = (Element) document.selectSingleNode("//jdbc/property[@name='password']");

        String driverValue = (String) driver.attribute("value").getData();
        String urlValue = (String) url.attribute("value").getData();
        String usernameValue = (String) username.attribute("value").getData();
        String passwordValue = (String) password.attribute("value").getData();

        BaseJdbc baseJdbc = new BaseJdbc();
        baseJdbc.setDriver(driverValue);
        baseJdbc.setUrl(urlValue);
        baseJdbc.setUser(usernameValue);
        baseJdbc.setPassword(passwordValue);

        return baseJdbc;
    }

    public ComplexJdbc getComplexJdbc()  {

        SAXReader saxReader = new SAXReader();
        // 在项目中相对的是以项目名为根路径
        Document document = null;
        try {
            document = saxReader.read(xmlPath);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        Element driver = (Element) document.selectSingleNode("//jdbc/property[@name='driver']");
        Element url = (Element) document.selectSingleNode("//jdbc/property[@name='url']");
        Element username = (Element) document.selectSingleNode("//jdbc/property[@name='username']");
        Element password = (Element) document.selectSingleNode("//jdbc/property[@name='password']");

        String driverValue = (String) driver.attribute("value").getData();
        String urlValue = (String) url.attribute("value").getData();
        String usernameValue = (String) username.attribute("value").getData();
        String passwordValue = (String) password.attribute("value").getData();

        ComplexJdbc complexJdbc = new ComplexJdbc();
        complexJdbc.setDriver(driverValue);
        complexJdbc.setUrl(urlValue);
        complexJdbc.setUser(usernameValue);
        complexJdbc.setPassword(passwordValue);

        return complexJdbc;
    }

}

5、相关配置

能做到把配置信息从 Java 代码中分离出来,是这几天的一个进展,后面会设法用 Java Bean 注解的方式取代 xml 配置
必备点:① id(标识)、② class(备反射)

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="student" class="framekworkTest.iocTest.bean.impl.Student" />
    <bean id="teacher" class="framekworkTest.iocTest.bean.impl.Teacher" />

    <jdbc>
        <property name="driver" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://120.79.197.133:3307/testspring?characterEncoding=utf-8" />
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </jdbc>

</beans>

5、测试效果

①实体
实体对应数据库中的表
这里写图片描述

package framekworkTest.daoTest.entity;

import core.Dao.tableAnnotation.Column;
import core.Dao.tableAnnotation.Id;
import core.Dao.tableAnnotation.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data // getter、setter
@AllArgsConstructor // 有参构造函数
@NoArgsConstructor // 无参构造函数
@Table("t_user")
public class User {

    @Id
    @Column("id")
    private Integer id;

    @Column("user_name") // sql中属性名
    private String userName; // Java中属性名

    @Column("password")
    private String password;

    @Column("department_id")
    private Integer departmentId;

}

②测试

package framekworkTest.daoTest;

import core.Dao.BaseCrud.BaseJdbc;
import core.factory.XmlBeanFactory;
import framekworkTest.daoTest.entity.User;
import org.junit.jupiter.api.Test;

import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class BaseJdbcTest {

    private BaseJdbc baseJdbc = new XmlBeanFactory("src/main/resources/spring.xml").getBaseJdbc();

    /**
     * 1.1、查
     * 实体中有多少个属性,就有多少个查询的条件
     * select * from t_user w
     * 返回类型:listhere 1=1 and password='123'
     */
    @Test
    public void queryByEntity() throws SQLException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
        User user1 = new User(null, "larger5", "123",null);
        List<Map<String, Object>> maps = baseJdbc.queryByEntity(user1);
        System.out.println(maps);
    }

    /**
     * 1.2、查 + 分页
     * 实体中有多少个属性,就有多少个查询的条件
     * select * from t_user where 1=1 and password='123' limit 0,2
     * 返回类型:list
     */
    @Test
    public void queryByEntityAndPage() throws SQLException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
        User user1 = new User(null, null, "123",null);
        List<Map<String, Object>> maps = baseJdbc.queryByEntityAndPage(user1, 1, 2);
        System.out.println(maps);
    }

    /**
     * 1.3、查数量
     * 配合分页使用,返回给前端
     * 实体中有多少个属性,就有多少个查询的条件
     * select count(*) from t_user where 1=1 and user_name='larger5' and password='123'
     * 返回类型:list
     */
    @Test
    public void countByEntity() throws SQLException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
        User user1 = new User(null, "larger5", "123",null);
        Integer count = baseJdbc.countByEntity(user1);
        System.out.println(count);
    }

    /**
     * 2、删
     * 实体中有多少个属性,就有多少个删除的条件
     * delete from t_user where 1=1 and id='6'
     * 返回类型:成功 true、失败 false
     */
    @Test
    public void deleteByEntity() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, SQLException, IllegalAccessException {
        User user2 = new User(6, null, null,null);
        Boolean aBoolean2 = baseJdbc.deleteByEntity(user2);
        System.out.println(aBoolean2);
    }

    /**
     * 3、增
     * 允许实体中有任意个null属性
     * 允许自定义id,但是别和原有的记录冲突
     * insert into t_user(user_name,password) values('55','998')
     * 返回类型:成功 true、失败 false
     */
    @Test
    public void insertByEntity() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, SQLException, IllegalAccessException {
        User user3 = new User(null, "55", "998",null);
        Boolean aBoolean3 = baseJdbc.insertByEntity(user3);
        System.out.println(aBoolean3);
    }

    /**
     * 4.1、改
     * 以第一个有 @Id 的为条件,其他的为改,实体中属性值为null的不改
     * update t_user set user_name='larger',password='5555' where id='13'
     * 返回类型:成功 true、失败 false
     */
    @Test
    public void updateByEntity() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, SQLException, IllegalAccessException {
        User user4 = new User(13, "larger", "5555",null);
        Boolean aBoolean4 = baseJdbc.updateByEntity(user4);
        System.out.println(aBoolean4);
    }

    /**
     * 4.2、改
     * 用于置空记录中的一些字段
     * update t_user set user_name=null,password='123' where id='12'
     */
    @Test
    public void updateByEntityWithAllColumnTest() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException, ClassNotFoundException {
        User user5 = new User(12, null, "123",null);
        Boolean aBoolean = baseJdbc.updateByEntityWithAllColumn(user5);
        System.out.println(aBoolean);
    }
}

三、小结

自从年初开始阅读 JavaEE 主流框架的阅读源码,有了一些经验:
如果直接买本书如《Spring 源码深度解析》,从头到尾的读下去,可能最后还在云里雾里

这里提供阅读源码的一种思路,就是先自己手动实现,尽量思考,做出一定效果出来,这时会有一定的成就感,
同时,最终可能会遇到瓶颈,或比较费时费力的地方,这时再去参考其他资料,源码,才会恍然大悟。

而那些从头到尾看源码并且看懂的,没有自己敲代码实现框架部分功能的,恐怕是自欺欺人吧

猜你喜欢

转载自blog.csdn.net/larger5/article/details/82633806