Flyinsky's Codes
1381 字
7 分钟
JavaWeb中持久层最佳实践

JavaWeb中持久层最佳实践#

​ 本博客之前写过很多关于Springboot的集成各种工具类(Mybatis,邮件发送,AI接口…),其过程也是非常简单,导入依赖,在application.yml中写入配置,再在代码中添加一些注解或配置类就实现了功能的集成,这过程如此简单得益于框架的先进。在JavaWeb开发中,没有了boot的自动配置,我们在使用这些依赖就需要多那么几步(也没多多少)。

​ 本篇讲的是持久层的最佳实践,主要就是两个:Apache DBUtils和Mybatis。前者是Apache开发的一个工具类,用于简化jdbc编程代码,加速开发周期,毕竟在早些年没有人愿意天天获取ResultSet,遍历一遍,获取对应的键(属性名)的值,最后利用构造函数得到我们需要的实体类。那样一个dao的代码量超级大,可读性也非常差。后者则是目前最流行的数据库框架,在Springboot的文章就有介绍,不赘述。

​ 上述呢只是CRUD的过程中用到的相关工具,在这之前,还有一个比较重要的东西便是数据库连接池,顾名思义就是一个管理数据库连接的池子,它能够有效管理数据库的连接,甚至可以进行性能监控,防止安全问题出现等功能…本文将使用Hikari数据库连接池,它是目前性能最好的连接池,其他的还有例如c3p0,druid…

前言#

​ 无论是Apache的工具类,还是Mybatis框架,实际上它们在操作时都是大同小异的,至少有一点是相通的:

[!TIP]

​ 选择语句往往会声明需要返回的实体类,这个过程中,框架或工具类(后续称框架)都会调用对应实体类的空构造函数,先new出一个啥内容也没有得空壳对象,然后将数据库中拿出来对应的值利用实体类的setter函数一个一个的赋值,所以你的实体类必须要有getter,setter以及空构造函数,墙裂推荐Lombok的@Data注解。

​ 那在插入语句或更新语句也是同理,框架也会调用其setter函数去拿到要插入实体类中的属性的值。

​ 总结就是我个人习惯在写实体类时,会打上一个@Data注解,让lombok为我隐式地生成getter和setter,然后写一个空形式参数列表的构造函数,后续有啥要用到的构造函数再慢慢过来加。

Haikri连接池#

先引入依赖:

		<dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>4.0.3</version>
        </dependency>

如果我们使用Apache DButil:

书写一个数据源工厂,便于我们在不同类中拿取数据源(DataSource)。

所有数据库的信息,例如链接,用户名,密码,驱动名称都在这个类中定义,在需要的时候,因其成员变量都静态的,所以可以直接调用IHikariDataSource.getDataSource()拿取,不需要重复的new。

/**
 * @author Flyinsky
 * @email w2084151024@gmail.com
 * @date 2024/10/9 13:13
 */
public class IHikariDataSource {
    @Getter
    private static com.zaxxer.hikari.HikariDataSource dataSource;

    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        config.setUsername("test");
        config.setPassword("test");
        config.setDriverClassName("com.mysql.cj.jdbc.Driver");
        config.setMaximumPoolSize(10);

        dataSource = new com.zaxxer.hikari.HikariDataSource(config);
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    public static void close() {
        dataSource.close();
    }
}

如果我们使用Mybatis,也需要创建一个数据源工程,再在resources目录下创建mybatis-config.xml配置文件:

[!WARNING]

注意xml中的datasource标签,你的包名和类名和我的肯定不一样要自己改

/**
 * @author Flyinsky
 * @email w2084151024@gmail.com
 * @date 2024/10/12 16:25
 */
public class HikariDataSourceFactory implements DataSourceFactory {
    private Properties props;

    @Override
    public void setProperties(Properties props) {
        this.props = props;
    }

    @Override
    public DataSource getDataSource() {
        HikariConfig config = new HikariConfig();
        config.setDriverClassName(props.getProperty("driver"));
        config.setJdbcUrl(props.getProperty("url"));
        config.setUsername(props.getProperty("username"));
        config.setPassword(props.getProperty("password"));
				//啥也不用改,自动读取xml中的信息
        return new HikariDataSource(config);
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="com.littlegarden.javaweb.util.HikariDataSourceFactory">
                <!--你的包名和类名和我的肯定不一样要自己改-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="test"/>
                <property name="password" value="test"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

这样就成功引入了连接池,后续的CRUD都是基于连接池创建的数据源或连接上操作。

Apache DBUtils#

​ 先引入依赖:

		<dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.8.1</version>
        </dependency>

​ 然后就可以开始书写DAO:

​ 下面是增删改的例子,update和增类似 区别会在最后讲。

    QueryRunner queryRunner = new QueryRunner(IHikariDataSource.getDataSource());//new一个工具类,传入连接池的数据源。
/**
     * 根据ID查询商品
     * @param id
     * @return 商品
     * @throws SQLException
     */
    public Item getItemById(Integer id) throws SQLException {
        String sql = "SELECT * FROM tb_items WHERE id = ?";//写法还是和jdbc一样,?表示待填入的查询参数
        Object[] params = {id};//定义一个基类数组,按照顺序填充sql语句中的?处的参数
        return queryRunner.query(sql,new BeanHandler<>(Item.class),params);//使用查询执行器执行语句,参数列表中按照sql语句,类处理器,查询参数数组的顺序填写。
    }
    /**
     * 获取所有商品
     * @return 商品列表
     * @throws SQLException
     */
    public List<Item> getAllItems() throws SQLException {
        String sql = "SELECT * FROM tb_items";
        return queryRunner.query(sql, new BeanListHandler<>(Item.class));//注意查询执行器的类处理器,如果返回的是一个集合(多条记录),需要用BeanListHandler而不是BeanHandler
    }
/**
     *  插入新对话列表记录
     * @param chatList
     * @return  新增记录的id的值
     */
    public BigInteger generateNewChatList(ChatList chatList) throws SQLException {
        String sql = "INSERT INTO tb_chatlist (userId, title, appId) VALUES (?,?,?)";
        Object[] params = {chatList.getUserId(),chatList.getTitle(),chatList.getAppId()};
        return queryRunner.insert(sql,new ScalarHandler<>(),params);
    }
//insert方法返回主键自增的id的值(如果有),而update返回影响的记录的行数