美文网首页springboot
spring和springboot项目启动执行sql文件

spring和springboot项目启动执行sql文件

作者: 宇宙小神特别萌 | 来源:发表于2019-12-04 15:37 被阅读0次
    spring项目启动执行sql文件目录.png

    介绍:此工具类支持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"/>
    
    

    相关文章

      网友评论

        本文标题:spring和springboot项目启动执行sql文件

        本文链接:https://www.haomeiwen.com/subject/hldlgctx.html