介绍:此工具类支持spring和springboot框架,支持druid/c3p0数据库连接池,支持mysql/oracle等常见数据库sql语法
数据库连接池
<!--druid 数据源连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!--c3p0 数据源连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
前提:sql文件是有顺序的。如:必须是先创建表,再插入数据。
*.sql文件项目中的位置:
- resources/db/mysql/*.sql
- resources/db/oralce/*.sql
默认.sql文件都是单条正常sql语法,不是多条或非基本sql语法:
例如resources/db/mysql/userentity.sql
-- 删表语句
drop table if exists userentity;
COMMIT;
-- 用户表,如果表不存在,则创建,id自增且是主键,username不能null
CREATE TABLE IF NOT EXISTS userentity(
id bigint not null,
username VARCHAR(50) not null,
age int,
createtime DATE,
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
COMMIT;
insert into USERENTITY(id,username,age) values (1,'小明',18);
insert into USERENTITY(id,username,age) values (2,'小刘',20);
COMMIT;
例如resources/db/oralce/userentity.sql
drop table userentity;
COMMIT;
-- 用户表
create table userentity
(
id NUMBER(19) not null
primary key,
username VARCHAR2(255 char),
createtime TIMESTAMP(6),
age NUMBER(19)
);
COMMIT;
-- userentity用户表数据准备
insert into USERENTITY(id,username,age) values (1,'小明',21);
insert into USERENTITY(id,username,age) values (2,'小刘',22);
COMMIT;
*.sql文件,是多条或非基本sql的语法:
例如:resources/db/oralce/userentity2.sql
-- 删除测试的表
declare
countTab number;
begin
--===============20191203==================start
-- 删除无用表 upper:小写字符转化成大写的函数
select count(*) into countTab from user_tables where table_name = upper('userentity');
if countTab = 1 then
execute immediate 'drop table userentity';
end if;
--===============20191203==================end
end;$$
-- 用户表
create table userentity
(
id NUMBER(19) not null
primary key,
username VARCHAR2(255 char),
createtime TIMESTAMP(6),
age NUMBER(19)
) $$
COMMIT $$
-- userentity用户表数据准备
insert into USERENTITY(id,username,age) values (1,'小明',21) $$
insert into USERENTITY(id,username,age) values (2,'小刘',22) $$
COMMIT $$
看代码:
SpringContextGetter.java
/**
* Date: 2019-12-03 14:19
* Author: zhengja
* Email: zhengja@dist.com.cn
* Desc:
*/
@Component
public class SpringContextGetter implements ApplicationContextAware {
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
情况1:druid+mysql/oralce
描述:默认.sql文件中的sql语句没有非基本sql语法。
代码演示:
@Slf4j
@Component
public class SchemaHandler {
@Autowired
private DataSource datasource;
@Autowired
private SpringContextGetter springContextGetter;
/**
* 执行判断数据源连接池
*/
public void execute() throws Exception {
//判断是druid 连接池
if (datasource instanceof DruidDataSource){
DruidDataSource druidDataSource = (DruidDataSource) this.datasource;
judgeDriver(druidDataSource.getDriverClassName());
}
}
/**
* 根据驱动判断是mysql/oracle的.sql文件
* @param driverClassName 驱动名称
*/
private void judgeDriver(String driverClassName) throws SQLException, IOException {
if (driverClassName.equals("com.mysql.jdbc.Driver") || driverClassName.equals("com.mysql.cj.jdbc.Driver")){
executeSqlFile("mysql");
}
if (driverClassName.equals("oracle.jdbc.OracleDriver") || driverClassName.equals("oracle.jdbc.driver.OracleDriver")){
executeSqlFile("oracle");
}
}
/**
* 执行sql文件
* @param dbname 位于classpath:db下的文件名称
*/
private void executeSqlFile(String dbname) throws SQLException, IOException {
File file = ResourceUtils.getFile("classpath:db"+File.separator+dbname);
if (!file.exists()){
log.info("不存在【 "+"classpath:db"+File.separator+dbname+"】文件");
return;
}
File[] files = file.listFiles();
for (File f : files){
String sqlRelativePath = "classpath:db"+File.separator+dbname+File.separator+f.getName();
Resource resource = springContextGetter.getApplicationContext().getResource(sqlRelativePath);
//一条sql语句,默认以";"结尾区分
ScriptUtils.executeSqlScript(this.datasource.getConnection(), new EncodedResource(resource,"UTF-8"));
}
}
}
情况2:c3p0+mysql/oracle
描述:默认.sql文件中的sql语句没有非基本sql语法。
代码演示:
@Slf4j
@Component
public class SchemaHandler {
@Autowired
private DataSource datasource;
@Autowired
private SpringContextGetter springContextGetter;
/**
* 执行判断数据源连接池
*/
public void execute() throws Exception {
//判断是c3p0 连接池
if (datasource instanceof ComboPooledDataSource){
ComboPooledDataSource comboPooledDataSource = (ComboPooledDataSource) this.datasource;
judgeDriver(comboPooledDataSource.getDriverClass());
}
}
//代码和上面一样
......
}
情况3:druid/c3p0+oracle/mysql
描述:默认.sql文件中的sql语句没有非基本sql语法。
代码演示:
@Slf4j
@Component
public class SchemaHandler {
@Autowired
private DataSource datasource;
@Autowired
private SpringContextGetter springContextGetter;
/**
* 执行判断数据源连接池
*/
public void execute() throws Exception {
//判断是c3p0/druid 连接池
if (datasource instanceof DruidDataSource){
DruidDataSource druidDataSource = (DruidDataSource) this.datasource;
judgeDriver(druidDataSource.getDriverClassName());
}
if (datasource instanceof ComboPooledDataSource){
ComboPooledDataSource comboPooledDataSource = (ComboPooledDataSource) this.datasource;
judgeDriver(comboPooledDataSource.getDriverClass());
}
}
//代码和上面一样
......
}
情况4:druid/c3p0+oracle/mysql
特殊情况:比如说有非基本sql语句出现,需要将默认分割符号";"替换成"$$"。
描述: .sql文件中的sql语句有非基本sql语法。
比如:oracle的sql语句是非基本sql语法,将默认分割符号";"替换成"$$"。
位置:resources/db/oralce/orcl-0-准备数据.sql
-- 删除测试的表
declare
countTab number;
begin
--===============20191203==================start
-- 删除无用表 upper:小写字符转化成大写的函数
select count(*) into countTab from user_tables where table_name = upper('userentity');
if countTab = 1 then
execute immediate 'drop table userentity';
end if;
--===============20191203==================end
end;$$
-- 用户表
create table userentity
(
id NUMBER(19) not null
primary key,
username VARCHAR2(255 char),
createtime TIMESTAMP(6),
age NUMBER(19)
) $$
COMMIT $$
-- userentity用户表数据准备
insert into USERENTITY(id,username,age) values (SEQ_MY_USER.NEXTVAL,'小明',21) $$
insert into USERENTITY(id,username,age) values (SEQ_MY_USER.NEXTVAL,'小刘',22) $$
COMMIT $$
代码演示:
@Slf4j
@Component
public class SchemaHandler {
@Autowired
private DataSource datasource;
@Autowired
private SpringContextGetter springContextGetter;
/**
* 执行判断数据源连接池
*/
public void execute() throws Exception {
//判断是c3p0/druid 连接池
if (datasource instanceof DruidDataSource){
DruidDataSource druidDataSource = (DruidDataSource) this.datasource;
judgeDriver(druidDataSource.getDriverClassName());
}
if (datasource instanceof ComboPooledDataSource){
ComboPooledDataSource comboPooledDataSource = (ComboPooledDataSource) this.datasource;
judgeDriver(comboPooledDataSource.getDriverClass());
}
}
/**
* 根据驱动判断是mysql/oracle的.sql文件
* @param driverClassName 驱动名称
*/
private void judgeDriver(String driverClassName) throws SQLException, IOException {
if (driverClassName.equals("com.mysql.jdbc.Driver") || driverClassName.equals("com.mysql.cj.jdbc.Driver")){
executeSqlFile("mysql");
}
if (driverClassName.equals("oracle.jdbc.OracleDriver") || driverClassName.equals("oracle.jdbc.driver.OracleDriver")){
executeSqlFile("oracle");
}
}
/**
* 执行sql文件
* @param dbname 位于classpath:db下的文件名称
*/
private void executeSqlFile(String dbname) throws SQLException, IOException {
File file = ResourceUtils.getFile("classpath:db"+File.separator+dbname);
if (!file.exists()){
log.info("不存在【 "+"classpath:db"+File.separator+dbname+"】文件");
return;
}
File[] files = file.listFiles();
if (dbname.equals("oracle")){
for (File f : files){
String sqlRelativePath = "classpath:db"+File.separator+dbname+File.separator+f.getName();
Resource resource = springContextGetter.getApplicationContext().getResource(sqlRelativePath);
//一条sql语句以"$$"结尾区分.执行oralce的存储过程 将'declare countCol number;'当初一条sql执行爆错,因默认以";"结尾是一条sql语句,更改成以"$$"分割作为一条sql语句
ScriptUtils.executeSqlScript(this.datasource.getConnection(), new EncodedResource(resource,"UTF-8"), false, false, "--", "$$", "/*", "*/");
}
}
if (dbname.equals("mysql")){
for (File f : files){
String sqlRelativePath = "classpath:db"+File.separator+dbname+File.separator+f.getName();
Resource resource = springContextGetter.getApplicationContext().getResource(sqlRelativePath);
//一条sql语句,默认以";"结尾区分
ScriptUtils.executeSqlScript(this.datasource.getConnection(), new EncodedResource(resource,"UTF-8"));
}
}
}
}
启动方式1:调用接口执行sql文件
@RestController
public class SqlController {
@Autowired
private SchemaHandler schemaHandler;
@GetMapping("execute/sql")
public Object executeSql() throws Exception {
schemaHandler.execute();
return "sql文件执行成功!";
}
}
启动方式2:项目启动时执行sql文件
/**
* Date: 2019-12-04 13:18
* Author: zhengja
* Email: zhengja@dist.com.cn
* Desc:在初始化Bean时,操作数据库执行sql文件
*/
@Component
public class InitSql implements InitializingBean {
@Value("${jdbc.isStartSql}")
private boolean isStartSql;
@Autowired
private SchemaHandler schemaHandler;
@Override
public void afterPropertiesSet() throws Exception {
if (isStartSql){
this.schemaHandler.execute();
}
}
}
spring的properties文件添加配置:
# 项目启动是否启动执行sql文件 true/false ,建议首次创建后false关闭,避免数据丢>失! jdbc.isStartSql=true
springboot的yml文件添加配置:
# 项目启动是否启动执行sql文件 true/false ,建议首次创建后false关闭,避免数据丢>失! jdbc: isStartSql: true
最后注意点:
使用druid连接池时,要把druid配置属性过滤器中的wall去掉,这是也是最简单粗暴的方式。当同时要使用wall需配合代理过滤器。
<!--wall防止sql注入,具有检测sql基本语法和执行多条sql语句的判断,会报异常--> <!--<property name="filters" value="stat,wall"/>--> <property name="filters" value="stat"/>
网友评论