【Java】Java核心 77:Dom4j 解析 XML综合案例

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云解析 DNS,旗舰版 1个月
简介: 1、需求需求:自定义dao层jdbc框架为了方便程序员操作数据库,让程序员更关注于sql代码层面和业务层面

1、需求

需求:自定义dao层jdbc框架

  • 为了方便程序员操作数据库,让程序员更关注于sql代码层面和业务层面


2、案例效果

f20a80286ac14a23bc734bd9c5ffc627.png

15573803b42443448a91140a0b0f0e61.png


39b0a36e917846c5a1ba81cdbcd1cc11.png

使用到的技术

- 反射

- 注解

- 动态代理

- xml解析:xpath


3、案例分析

自定义jdbc框架开发步骤:


1、通过软配置方式,和数据库连接


解析xml配置文件,获得:driver、url、username、password

德鲁伊连接池

根据配置文件中的参数,创建连接池对象

2、创建@Select注解


解析@Select注解中value值:select查询语句

3、创建映射类Mapper


把从@Select注解中解析出来的select查询语句,赋值给Mapper中的sql成员变量

4、创建SqlSession类


提供getMapper()方法,用来获取代理对象

说明:程序员在获取到代理对象后,利用代理对象调用某个方法时,会被代理对象拦截处理

4、自定义JDBC框架-代码实现

4.1、Configuration

/*配置类
  1. 解析XML文件
  2. 创建德鲁伊连接池
*/
public class Configuration {
    /* 定义数据库连接对象相关属性 */
    private String driver;//驱动
    private String url;//连接地址
    private String username;//登录名
    private String password;//密码
    /* Mapper接口的全名称 */
    private String interName;
    /* 数据库连接池对象 */
    private DataSource dataSource;
    /* 映射类对象 */
    private Mapper mapper = new Mapper();
    //无参构造方法
    public Configuration() {
        try {
            //解析"config.xml"文件
            SAXReader reader = new SAXReader();
            InputStream is = Configuration.class.getClassLoader().getResourceAsStream("config.xml");
            Document doc = reader.read(is);
            //调用自定义方法: 将核心配置文件中的数据封装到Configuration类的属性中
            loadConfigXml(doc);
            //调用自定义方法: 初始化数据库连接池对象
            createDataSource();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //初始化数据库连接池对象
    private void createDataSource() throws Exception {
        //创建c3p0核心类对象
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        //使用对象调用方法将四大连接参数给数据库连接池
        cpds.setDriverClass(driver);
        cpds.setJdbcUrl(url);
        cpds.setUser(username);
        cpds.setPassword(password);
        //将cpds赋值给成员变量ds
        this.dataSource = cpds;//数据库连接池对象
    }
    //将核心配置文件中的数据封装到Configuration类属性中
    private void loadConfigXml(Document doc) {
        /*
        //使用document对象调用方法获取property标签:
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/itheima"/>
        <property name="username" value="root"/>
        <property name="password" value="itheima"/>
        */
        List<Node> list = doc.selectNodes("//property");
        //遍历List集合
        for (Node node : list) {
            //强制转换
            Element e = (Element) node;
            //获取property标签的name属性值
            String name = e.attributeValue("name");//双引号中的name是property标签的name属性 driver
            //获取property标签的value属性值
            String value = e.attributeValue("value");//双引号中的value是property标签的value属性 com.mysql.jdbc.Driver
            //将value的值赋值给成员变量
            switch (name) {
                case "driver":
                    this.driver = value;//数据库驱动
                    break;
                case "url":
                    this.url = value;//连接url
                    break;
                case "username":
                    this.username = value;//登录名
                    break;
                case "password":
                    this.password = value;//密码
                    break;
            }
        }
        //<package name="xxx.xxx.UserMapper"></package>
        Node node = doc.selectSingleNode("//package");
        Element e = (Element) node;
        //Mapper接口的全名称
        this.interName = e.attributeValue("name");//"xxx.xxx.UserMapper"
    }
    public String getDriver() {
        return driver;
    }
    public void setDriver(String driver) {
        this.driver = driver;
    }
    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 String getInterName() {
        return interName;
    }
    public void setInterName(String interName) {
        this.interName = interName;
    }
    public DataSource getDataSource() {
        return dataSource;
    }
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    public Mapper getMapper() {
        return mapper;
    }
    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }
}

4.2、注解

@Select

//查询时使用的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
    String[] value();
}

@ResultType

//查询结果的封装类型
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResultType {
    String value();
}

4.3、映射类:Mapper

package cn.itcast.config;
public class Mapper {
    private String sql;//存储sql语句
    private String resultType;//结果类型
    public Mapper() {
    }
    public Mapper(String sql) {
        this.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;
    }
}

4.4、SqlSession类

@SuppressWarnings("all")
public class SqlSession {
    /**
     * 动态代理
     */
    public <T> T getMapper(Class<T> clazz) {
        //类加载器: 负责加载代理类到内存中
        ClassLoader classLoader = SqlSession.class.getClassLoader();
        //父接口
        Class[] interfaces = {clazz};
        T mapperProxy = (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            //在调用方法时,代理对象执行invoke方法,返回List
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //创建核心配置类对象
                Configuration config = new Configuration();
                /******** 解析 @Select、@ResultType *******/
                Class clazz = Class.forName(config.getInterName());
                Method[] methods = clazz.getMethods();
                //遍历数组
                for (Method m : methods) {
                    //判断是否有注解
                    boolean boo = m.isAnnotationPresent(Select.class);
                    boolean boo2 = m.isAnnotationPresent(ResultType.class);
                    if (boo && boo2) {
                        //获取@Select注解对象
                        Select select = m.getAnnotation(Select.class);
                        //获取属性值
                        String[] value = select.value();//{"select * from user"}
                        String sql = value[0];
                        //给Mapper对象中的sql成员变量赋值
                        config.getMapper().setSql(sql);
                        //获取@ResultType注解对象
                        ResultType resultType = m.getAnnotation(ResultType.class);
                        String type = resultType.value();//获取属性值
                        config.getMapper().setResultType(type);
                    }
                }
                /*******************************/
                //获取映射对象
                Mapper mapper = config.getMapper();
                //利用映射对象,获取sql语句
                String sql = mapper.getSql();
                //利用映射对象,获取类型
                String resultType = mapper.getResultType();
                Class cl = Class.forName(resultType);
                //获取数据库连接池对象
                DataSource ds = config.getDataSource();
                //利用连接池对象,获取Connection对象
                Connection conn = ds.getConnection();
                //调用自定义方法: 执行sql查询语句
                List list = queryForList(conn, sql, cl);
                return list;
            }
        });
        //代理对象返回给getMapper的调用者     UserMapper mapper = sqlSession.getMapper(UserMapper.class);//mapperProxy
        return mapperProxy;
    }
   public List queryForList(Connection conn, String sql, Class clazz) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        List userList = new ArrayList();
        //通过连接对象得到预编译的语句对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //执行SQL语句,得到结果集
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            //获取构造方法对象,并实例化
            Object user = clazz.getConstructor().newInstance();
            //获取所有的成员变量(包含私有成员变量)
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) { //field可以是:id name passwd age gender adddate
                //得到成员变量的名字
                String name = field.getName();
                //暴力破解: 取消权限检查
                field.setAccessible(true);
                //rs.getObject(name) 表示根据数据表的列名取出数据表中的列值 因为User类中的成员变量名必须和数据表列名一致
                //例如: name 的值是birthday 那么这里 rs.getObject(name)---》rs.getObject("age")获取数据表的年龄20
                Object table_value = rs.getObject(name);
                //void set(Object obj, Object value)给成员变量赋值,参数1:对象名 参数2:要赋的值
                field.set(user, table_value);
            }
            //user对象添加到集合中
            userList.add(user);
        }
        //释放资源
        rs.close();
        ps.close();
        conn.close();
        //返回集合
        return userList;
    }
}   

5、自定义JDBC框架的使用


5.1、数据表

CREATE TABLE user (
  id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name varchar(30) DEFAULT NULL,
  passwd varchar(20) DEFAULT NULL,
  age int(3) DEFAULT NULL,
  gender varchar(2) DEFAULT NULL,
  adddate date DEFAULT NULL
);
# 测试数据
INSERT INTO user VALUES (null, 'itcast', '123123', '10', '男', '2020-12-11');

5.2、创建实体类

public class User {
    private int id;
    private String name;
    private String passwd;
    private int age;
    private String gender;
    private Date adddate;
    public User() {
    }
    public User(int id, String name, String passwd, int age, String gender, Date adddate) {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
        this.age = age;
        this.gender = gender;
        this.adddate = adddate;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public Date getAdddate() {
        return adddate;
    }
    public void setAdddate(Date adddate) {
        this.adddate = adddate;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", passwd='" + passwd + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", adddate=" + adddate +
                '}';
    }
}

5.3、UserMapper接口

public interface UserMapper {
    //查询所有用户
    @Select("select * from user")
    @ResultType("com.itcast.pojo.User")
    public abstract List queryAllUser();
}

5.4、配置文件:config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--数据源-->
    <dataSource>
        <!--驱动-->
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <!--地址-->
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/itheima"/>
        <!--用户名-->
        <property name="username" value="root"/>
        <!--密码-->
        <property name="password" value="itheima"/>
    </dataSource>
    <!--加载映射接口-->
    <mappers>
        <package name="com.itcast.mapper.UserMapper"></package>
    </mappers>
</configuration>

5.5、测试类

public class Test1 {
    @Test
    public void testSelect() {
        //实例化
        SqlSession sqlSession = new SqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.queryAllUser();
        for (User u : users) {
            System.out.println(u);
        }
    }
}

assword" value=“itheima”/>

<!--加载映射接口-->
<mappers>
    <package name="com.itcast.mapper.UserMapper"></package>
</mappers>

5.5、测试类

public class Test1 {
    @Test
    public void testSelect() {
        //实例化
        SqlSession sqlSession = new SqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.queryAllUser();
        for (User u : users) {
            System.out.println(u);
        }
    }
}

相关文章
|
5天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
40 6
|
12天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
2天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
12 4
|
1天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
9 2
|
5天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
10天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
9天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
27 2
|
9天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
10天前
|
JavaScript 前端开发
|
14天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
29 1

推荐镜像

更多