搭建一套基于Java的web开发框架-初始化数据库表
搭建一套基于Java的web开发框架-初始化数据库表
简介
主框架目前就两大功能块,其实这么简单点功能也可以不使用数据库,但想想其它功能插件需要使用数据库,还需要支持多种常用的数据库软件。在Solon官网看到有这些支持数据库操作的第三方库:
插件 | 说明 |
---|---|
activerecord-solon-plugin | 基于 activerecord 适配的插件,主要对接数据源与事务(自主内核) |
beetlsql-solon-plugin | 基于 beetlsql 适配的插件,主要对接数据源与事务(自主内核) |
sqltoy-solon-plugin | 基于 sqltoy 适配的插件,主要对接数据源与事务(自主内核) |
dbvisitor-solon-plugin | 基于 hasordb 适配的插件,主要对接数据源与事务(自主内核) |
hibernate-solon-plugin | 基于 hibernate 适配的插件,主要对接数据源与事务 |
mybatis-solon-plugin | 基于 mybatis 适配的插件,主要对接数据源与事务 |
mybatis-plus-solon-plugin | 基于 mybatis-plus 适配的插件,主要对接数据源与事务 |
mybatis-plus-extension-solon-plugin | 基于 mybatis-plus 及扩展适配的插件,主要对接数据源与事务 |
mybatis-flex-solon-plugin | 基于 mybatis-flex 适配的插件,主要对接数据源与事务 |
mybatis-mp-solon-plugin | 基于 mybatis-mp 适配的插件,主要对接数据源与事务 |
mybatis-plus-join-solon-plugin | 基于 mybatis-plus-join 适配的插件,主要对接数据源与事务 |
fastmybatis-solon-plugin | 基于 fastmybatis 适配的插件,主要对接数据源与事务 |
easy-query-solon-plugin | 基于 easy-query 适配的插件,主要对接数据源与事务(自主内核) |
bean-searcher-solon-plugin | 基于 bean-searcher 适配的插件 |
stream-plugin-mp-solon | 基于 stream-query 适配的插件 |
wood-solon-plugin | 基于 wood 适配的插件,主要对接数据源与事务(自主内核) |
但我在导航菜单中还看到一些带双冒号的数据库插件没在上面列出来,都粗略的看了下简介,我采用::anyline-environment-solon-plugin
插件,它可以直接操作表的元数据,我可以直接使用它来初始化主框架需要创建的表结构,增删改查的操作各插件都相差不多,anyline要使用指定数据库需要添加对应的jar依赖。
数据库初始化实操
1、在maven配置文件pom.xml中加入HikariCP数据连接池和anyline插件的依赖,然后刷新idea的maven依赖
<!-- 数据库连接池-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!-- anyline数据库插件-->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-environment-solon-plugin</artifactId>
<version>8.7.2-20240810</version>
</dependency>
2、添加对应的jdbc依赖和anyline数据库适配依赖,我这里添加了SQLite、Mysql、PostgreSql三种
<!-- (1).MySQL适配器 -->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-mysql</artifactId>
<version>8.7.2-20240810</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- (2).Postgresql适配器 -->
<!-- https://mvnrepository.com/artifact/org.anyline/anyline-data-jdbc-postgresql -->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-postgresql</artifactId>
<version>8.7.2-20240810</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
</dependency>
<!-- (3).Sqlite适配器 -->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-sqlite</artifactId>
<version>8.7.2-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.46.0.1</version>
</dependency>
3、在src/main/java/site/xiweihai/framework目录下创建InitDb类,代码中都有注释,用通义千问生成的,很详细
package site.xiweihai.framework;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.anyline.data.datasource.DataSourceHolder;
import org.anyline.metadata.Table;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import javax.sql.DataSource;
/**
* 初始化数据库执行类,通过界面传递数据库参数进行数据库的初始化
* @author hai
* @since 2024-08-12
*/
public class InitDb {
/**
* 初始化数据库连接并创建表
* 本函数用于配置数据库连接池,并通过连接池创建数据源,进而注册到数据源持有器中,
* 以便后续可以通过服务代理获取服务并执行DDL操作创建表
*
* @param driver 数据库驱动类名
* @param url 数据库连接URL
* @param user 数据库用户名
* @param password 数据库密码
*/
public static void run(String driver, String url, String user, String password) {
// 配置连接池
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(driver);
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(user);
hikariConfig.setPassword(password);
// 创建数据源
DataSource ds_sso = new HikariDataSource(hikariConfig);
// 定义临时数据库标识
String tempDb = "db";
try {
// 注册数据源到持有器
DataSourceHolder.reg(tempDb, ds_sso);
// 通过服务代理获取服务
AnylineService service = ServiceProxy.service(tempDb);
// 创建表结构
Table menuTable = createMenuTable();
service.ddl().create(menuTable);
Table pluginTable = createPluginTable();
service.ddl().create(pluginTable);
} catch (Exception e) {
// 异常处理:抛出运行时异常
throw new RuntimeException(e);
}
}
/**
* 创建插件信息表
* 该方法用于构建存储插件信息的表结构,包括插件的编号、名称、版本等信息
*
* @return Table 返回一个包含插件信息表结构的Table对象
*/
private static Table createPluginTable() {
Table table = new Table("FRAMEWORK_PLUGIN"); // 创建表,表名为"FRAMEWORK_PLUGIN"
// 添加列,设置列名为"`ID`",类型为BIGINT,允许自增,设为主键,注释为"插件编号"
table.addColumn("`ID`", "BIGINT").autoIncrement(true).setPrimary(true).setComment("插件编号");
// 添加列,设置列名为"`NAME`",类型为VARCHAR(50),注释为"插件名称"
table.addColumn("`NAME`", "VARCHAR(50)").setComment("插件名称");
// 添加列,设置列名为"`VERSION`",类型为VARCHAR(50),注释为"插件版本"
table.addColumn("`VERSION`", "VARCHAR(50)").setComment("插件版本");
// 添加列,设置列名为"`DESC`",类型为VARCHAR(255),注释为"插件说明"
table.addColumn("`DESC`", "VARCHAR(255)").setComment("插件说明");
// 添加列,设置列名为"`PATH`",类型为VARCHAR(500),不允许为空,注释为"插件存储路径"
table.addColumn("`PATH`", "VARCHAR(500)").nullable(false).setComment("插件存储路径");
// 添加列,设置列名为"`STATUS`",类型为INT(11),不允许为空,注释为"插件状态"
table.addColumn("`STATUS`", "INT(11)").nullable(false).setComment("插件状态");
// 添加列,设置列名为"`UPDATE_TIME`",类型为DATETIME,注释为"更新时间"
table.addColumn("`UPDATE_TIME`", "DATETIME").setComment("更新时间");
// 设置表的注释为"插件信息存储表"
table.setComment("插件信息存储表");
// 设置表的字符集为"utf8"
table.setCharset("utf8");
return table; // 返回构建好的表结构对象
}
/**
* 创建菜单信息表
* 该方法定义了数据库中用于存储菜单信息的表的结构
* 包括字段名称、字段类型、主键、自增长等属性
* 以及表的注释和字符集设置
*
* @return 返回创建好的表对象
*/
private static Table createMenuTable() {
// 创建一个名为"FRAMEWORK_MENU"的表对象
Table table = new Table("FRAMEWORK_MENU");
// 添加一个名为"ID"的列,类型为BIGINT,设置为自增长和主键,注释为"菜单编号"
table.addColumn("`ID`", "BIGINT").autoIncrement(true).setPrimary(true).setComment("菜单编号");
// 添加一个名为"NAME"的列,类型为VARCHAR(50),注释为"菜单名称"
table.addColumn("`NAME`", "VARCHAR(50)").setComment("菜单名称");
// 添加一个名为"PARENT_ID"的列,类型为BIGINT,注释为"父菜单编号"
table.addColumn("`PARENT_ID`", "BIGINT").setComment("父菜单编号");
// 添加一个名为"URL"的列,类型为VARCHAR(500),注释为"菜单URL"
table.addColumn("`URL`", "VARCHAR(500)").setComment("菜单URL");
// 添加一个名为"ICON"的列,类型为VARCHAR(50),注释为"菜单图标"
table.addColumn("`ICON`", "VARCHAR(50)").setComment("菜单图标");
// 添加一个名为"TYPE"的列,类型为INT(11),注释为"菜单类型"
table.addColumn("`TYPE`", "INT(11)").setComment("菜单类型");
// 添加一个名为"ORDER"的列,类型为INT(11),注释为"菜单排序"
table.addColumn("`ORDER`", "INT(11)").setComment("菜单排序");
// 添加一个名为"STATUS"的列,类型为INT(11),注释为"菜单状态"
table.addColumn("`STATUS`", "INT(11)").setComment("菜单状态");
// 添加一个名为"UPDATE_TIME"的列,类型为DATETIME,注释为"更新时间"
table.addColumn("`UPDATE_TIME`", "DATETIME").setComment("更新时间");
// 设置表的注释为"菜单信息存储表"
table.setComment("菜单信息存储表");
// 设置表的字符集为utf8
table.setCharset("utf8");
// 返回创建好的表对象
return table;
}
}
4、使用docker搭建数据库测试环境,创建docker/test/docker-compose.yml文件,在test目录执行docker-compose up -d 命令启动数据库
version: '3'
services:
mysql:
image: mysql:8.0
ports:
- "4000:3306"
environment:
MYSQL_ROOT_PASSWORD: 123456
postgres:
image: postgres:13.2
ports:
- "4001:5432"
environment:
POSTGRES_PASSWORD: 123456
5、创建单元测试类,使用idea在InitDb类中快捷创建测试类InitDbTest,先添加mysql的创建测试
import org.anyline.adapter.init.DefaultEnvironmentWorker;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.noear.solon.annotation.Import;
import org.noear.solon.test.SolonTest;
import static org.junit.jupiter.api.Assertions.*;
class InitDbTest {
@BeforeAll
static void init() {
// 启动数据库框架默认的环境工作器
DefaultEnvironmentWorker.start();
}
@Test
void mysqlInit() {
InitDb.run("com.mysql.cj.jdbc.Driver", "jdbc:mysql://127.0.0.1:4000/framework?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai", "root", "123456");
AnylineService service = ServiceProxy.service("db");
assertEquals(1,service.tables("FRAMEWORK_MENU").size());
assertEquals(1,service.tables("FRAMEWORK_PLUGIN").size());
}
}
运行一下测试,看到直接报错,不错不错果然没有那么顺利的
看到错误信息很明显是缺少类jackson
的依赖,在maven库里搜索下添加如下依赖,刷新下maven
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
继续执行测试类,嗯,又报错啦,看看错误
哦,是jackson的日期处理需要jsr310
这个依赖库,继续搜索下添加依赖,刷新
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.2</version>
</dependency>
继续,执行测试,不出意外又有意外了,看看错误
哦,没建数据库,在docker-compose.yml中添加MYSQL_DATABASE: framework
变量,docker-compose down
先停止,再使用docker-compose up -d
启动,继续执行测试类,这次对啦
6、添加postgresql的测试
@Test
void postgresqlInit() {
InitDb.run("org.postgresql.Driver", "jdbc:postgresql://127.0.0.1:4001/", "postgres", "123456");
AnylineService service = ServiceProxy.service("db");
assertEquals(1,service.tables("FRAMEWORK_MENU").size());
assertEquals(1,service.tables("FRAMEWORK_PLUGIN").size());
}
嗯,报错啦
额,postgre不支持反引号,改吧,不使用特殊字段了,修改的InitDb类如下所示:
package site.xiweihai.framework;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.anyline.data.datasource.DataSourceHolder;
import org.anyline.metadata.Table;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import javax.sql.DataSource;
/**
* 初始化数据库执行类,通过界面传递数据库参数进行数据库的初始化
* @author hai
* @since 2024-08-12
*/
public class InitDb {
/**
* 初始化数据库连接并创建表
* 本函数用于配置数据库连接池,并通过连接池创建数据源,进而注册到数据源持有器中,
* 以便后续可以通过服务代理获取服务并执行DDL操作创建表
*
* @param driver 数据库驱动类名
* @param url 数据库连接URL
* @param user 数据库用户名
* @param password 数据库密码
*/
public static void run(String driver, String url, String user, String password) {
// 配置连接池
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(driver);
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(user);
hikariConfig.setPassword(password);
// 创建数据源
DataSource ds_sso = new HikariDataSource(hikariConfig);
// 定义临时数据库标识
String tempDb = "db";
try {
// 注册数据源到持有器
DataSourceHolder.reg(tempDb, ds_sso);
// 通过服务代理获取服务
AnylineService service = ServiceProxy.service(tempDb);
// 创建表结构
Table menuTable = createMenuTable();
service.ddl().create(menuTable);
Table pluginTable = createPluginTable();
service.ddl().create(pluginTable);
} catch (Exception e) {
// 异常处理:抛出运行时异常
throw new RuntimeException(e);
}
}
/**
* 创建插件信息表
* 该方法用于构建存储插件信息的表结构,包括插件的编号、名称、版本等信息
*
* @return Table 返回一个包含插件信息表结构的Table对象
*/
private static Table createPluginTable() {
Table table = new Table("FRAMEWORK_PLUGIN"); // 创建表,表名为"FRAMEWORK_PLUGIN"
// 添加列,设置列名为"ID",类型为BIGINT,允许自增,设为主键,注释为"插件编号"
table.addColumn("ID", "BIGINT").autoIncrement(true).setPrimary(true).setComment("插件编号");
// 添加列,设置列名为"NAME",类型为VARCHAR(50),注释为"插件名称"
table.addColumn("PLUGIN_NAME", "VARCHAR(50)").setComment("插件名称");
// 添加列,设置列名为"VERSION",类型为VARCHAR(50),注释为"插件版本"
table.addColumn("PLUGIN_VERSION", "VARCHAR(50)").setComment("插件版本");
// 添加列,设置列名为"DESC",类型为VARCHAR(255),注释为"插件说明"
table.addColumn("PLUGIN_DESC", "VARCHAR(255)").setComment("插件说明");
// 添加列,设置列名为"PATH",类型为VARCHAR(500),不允许为空,注释为"插件存储路径"
table.addColumn("PLUGIN_PATH", "VARCHAR(500)").nullable(false).setComment("插件存储路径");
// 添加列,设置列名为"STATUS",类型为INT(11),不允许为空,注释为"插件状态"
table.addColumn("PLUGIN_STATUS", "INT(11)").nullable(false).setComment("插件状态");
// 添加列,设置列名为"UPDATE_TIME",类型为DATETIME,注释为"更新时间"
table.addColumn("UPDATE_TIME", "DATETIME").setComment("更新时间");
// 设置表的注释为"插件信息存储表"
table.setComment("插件信息存储表");
// 设置表的字符集为"utf8"
table.setCharset("utf8");
return table; // 返回构建好的表结构对象
}
/**
* 创建菜单信息表
* 该方法定义了数据库中用于存储菜单信息的表的结构
* 包括字段名称、字段类型、主键、自增长等属性
* 以及表的注释和字符集设置
*
* @return 返回创建好的表对象
*/
private static Table createMenuTable() {
// 创建一个名为"FRAMEWORK_MENU"的表对象
Table table = new Table("FRAMEWORK_MENU");
// 添加一个名为"ID"的列,类型为BIGINT,设置为自增长和主键,注释为"菜单编号"
table.addColumn("ID", "BIGINT").autoIncrement(true).setPrimary(true).setComment("菜单编号");
// 添加一个名为"NAME"的列,类型为VARCHAR(50),注释为"菜单名称"
table.addColumn("MENU_NAME", "VARCHAR(50)").setComment("菜单名称");
// 添加一个名为"PARENT_ID"的列,类型为BIGINT,注释为"父菜单编号"
table.addColumn("PARENT_ID", "BIGINT").setComment("父菜单编号");
// 添加一个名为"URL"的列,类型为VARCHAR(500),注释为"菜单URL"
table.addColumn("MENU_URL", "VARCHAR(500)").setComment("菜单URL");
// 添加一个名为"ICON"的列,类型为VARCHAR(50),注释为"菜单图标"
table.addColumn("MENU_ICON", "VARCHAR(50)").setComment("菜单图标");
// 添加一个名为"TYPE"的列,类型为INT(11),注释为"菜单类型"
table.addColumn("MENU_TYPE", "INT(11)").setComment("菜单类型");
// 添加一个名为"ORDER"的列,类型为INT(11),注释为"菜单排序"
table.addColumn("MENU_ORDER", "INT(11)").setComment("菜单排序");
// 添加一个名为"STATUS"的列,类型为INT(11),注释为"菜单状态"
table.addColumn("MENU_STATUS", "INT(11)").setComment("菜单状态");
// 添加一个名为"UPDATE_TIME"的列,类型为DATETIME,注释为"更新时间"
table.addColumn("UPDATE_TIME", "DATETIME").setComment("更新时间");
// 设置表的注释为"菜单信息存储表"
table.setComment("菜单信息存储表");
// 设置表的字符集为utf8
table.setCharset("utf8");
// 返回创建好的表对象
return table;
}
}
OK,两个测试都通过啦
7、添加sqlite测试,因为sqlite比较特殊可以直接创建数据库文件并读取,不需要安装对应的数据库软件,所以这里直接使用文件
@Test
void sqliteInit() {
InitDb.run("org.sqlite.JDBC", "jdbc:sqlite:db.db", "", "");
AnylineService service = ServiceProxy.service("db");
assertEquals(1,service.tables("FRAMEWORK_MENU").size());
assertEquals(1,service.tables("FRAMEWORK_PLUGIN").size());
}
OK,添加的三个数据库的初始化测试全部成功啦
结语
我这里写的测试类比较简单,创建表的语句这些都是由anyline生成,所以也没必要进行每个字段的比对,下一篇写个界面来对接这些初始化数据库吧
标题:搭建一套基于Java的web开发框架-初始化数据库表
作者:haizhilingyu
地址:https://xiweihai.site/articles/2024/08/14/1723622041113.html