在实际项目中,我们的数据库表中很有可能需要初始化一些信息。以便直接使用。
自定义实现数据初始化。这种方式就是自己实现,可用性高。可以自定义实现各种实现的功能。
Spring自动初始化
spring 可以在程序启动的时候执行初始化脚本。
配置项:
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/test
spring.datasource.name=test
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.hibernate.ddl-auto=update
#启动时需要初始化的建表语句--每次启动都会执行,一个一个语句执行,直到报错或者执行完成 spring.datasource.schema=classpath:initTable.sql #初始化的数据--每次启动都会执行,一个一个语句执行,直到报错或者执行完成 spring.datasource.data=classpath:data-mysql.sql #配置aways才会执行初始化语句 spring.datasource.initialization-mode=always #报错是否继续执行-需要配置true否则会导致已经执行过初始化语句的项目重启失败 spring.datasource.continue-on-error=true
spring.jpa.show-sql =false
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
spring.datasource.schema :配置的是建表的语句的文件路径。文件中的sql语句是一句一句执行的。失败了就会跳过。
spring.datasource.data :配置的是初始化数据的sql语句的文件路径。文件中的sql语句是一句一句执行的。失败了就会跳过。
spring.datasource.initialization-mode:值必须为aways这样才会执行,spring.datasource.schema和spring.datasource.data的配置。
spring.datasource.continue-on-error :需要配置为true。因为spring.datasource.schema和spring.datasource.data的配置每次重启都会执行。所以后续一定会报错,如果不是true,就导致执行失败程序启动失败。
自定义实现
这种方式实现中,我们需要有一张表记录是否已经初始化了数据库。当然不一定以需要记录在表中,也可以记录在文件,redis等可持久化服务中。宜jpa为例实现。
启动初始化记录表
定义一个表记录是否已经初始化,在程序启动时,判断表中是否有数据来决定是否初始化。
@Data
@Entity
@Table(name = "DB_VERSION")
public class DbVersionEntity {@Id@Column(name = "ID")private String id;/*** 版本号*/@Column(name = "APP_VERSION")private String appVersion;/*** 备注*/@Column(name = "REMARK")private String remark;/*** 更新时间*/@Column(name = "UPDATE_TIME")private Date updateTime = new Date();
访问类 Repository
@Repository
public interface DbVersionRepository extends JpaRepository<DbVersionEntity, Integer> {}
初始化Sql执行代码
这里提供了方法判断,数据库的类型。我们需要根据书库类型提前写好需要执行的sql语句。后面根据数据库执行对应的脚本。
@Repository
public class DbInfoRepository{@PersistenceContextprivate EntityManager entityManager;public DBType getCurrentDialect() {DBType dbType = DBType.POSTGRESQL;Map properties = this.entityManager.getEntityManagerFactory().getProperties();Object dialect = properties.get("hibernate.dialect");if (null != dialect) {String dialectStr = dialect.toString().trim().toLowerCase();if (StringUtils.isNotBlank(dialectStr)) {if (dialectStr.contains("mysql"))dbType = DBType.MYSQL;else if (dialectStr.contains("postgresql"))dbType = DBType.POSTGRESQL;else if (dialectStr.contains("oracle"))dbType = DBType.ORACLE;else if (dialectStr.contains("sqlserver"))dbType = DBType.SQLSERVER;else if (dialectStr.contains("postgis"))dbType = DBType.POSTGIS;else if (dialectStr.contains("hsql"))dbType = DBType.HSQL;else if (dialectStr.contains("h2"))dbType = DBType.H2;else if (dialectStr.contains("db2"))dbType = DBType.DB2;else if (dialectStr.contains("derby"))dbType = DBType.DERBY;else if (dialectStr.contains("sybase")) {dbType = DBType.SYBASE;}}}return dbType;}@Transactionalpublic void executeSql(String sql) {// 执行JDBC的数据批量保存Work jdbcWork = new Work() {public void execute(Connection connection) throws SQLException {Statement statement = null;try {statement = connection.createStatement();statement.execute(sql);} finally {if (statement != null) {statement.close();}}}};//执行插入数据的workthis.entityManager.unwrap(Session.class).doWork(jdbcWork);}}
加载文件、解析sql的方法
这里提供解析项目初始化文件位置的方法。执行sql语句的方法。
@Service
public class DbService {private static Logger LOGGER = LoggerFactory.getLogger(DbService.class);@Autowiredprivate DbInfoRepository dbInfoRepository;/*** 执行sql数据库脚本*/public boolean excuteUpgradeSqlFile(String fileName, DBType dbType) {Boolean sqlFlag = true; List<String> sqlList = readUpgradeSqlFile(fileName, dbType);if(sqlList!=null && sqlList.size()>0){for(String sql:sqlList){try {dbInfoRepository.executeSql(sql);} catch (Exception e) {LOGGER.error("执行数据库语句异常," + e.getMessage() + " \n SQL Language: " + sql);continue;}}}return sqlFlag;}/*** 读取sql数据库脚本*/private List<String> readUpgradeSqlFile(String fileName, DBType dbType) {List<String> sqlList = new ArrayList<String>();BufferedReader in = null;try {InputStream inputStream = getResourceAsStream(fileName);if(inputStream != null) {in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));String str;while ((str = in.readLine()) != null) {//空行或者注释行放过,所以注释不要超过一行,或者每行都用注释表示if (str.trim().isEmpty() || str.trim().startsWith("--")) {continue;}if (dbType == DBType.ORACLE) {str = str.trim().substring(0, str.length() - 1);}sqlList.add(str);}inputStream.close();} else {LOGGER.error("初始文件不存在");}return sqlList;} catch (IOException e) {LOGGER.error("读取初始化sql文件异常:"+ e.getMessage());}finally{try {if(in != null){in.close();}} catch (IOException e) {LOGGER.error(e.getMessage());}}return null;}/*** 获取指定文件流* @param path 文件路径* @return*/private InputStream getResourceAsStream(String path) {try {ResourceLoader resourceLoader = new ClassPathScanningCandidateComponentProvider(false).getResourceLoader();Resource resource = resourceLoader.getResource(path);if(resource.exists()) return resource.getInputStream();} catch (Exception e) {LOGGER.error(path + " not found");}return null;}
}
程序启动执行执行代码
@Component()
@Order(1)
public class DbInfoInitTask implements CommandLineRunner {private static HikGaLogger logger = HikGaLoggerFactory.getLogger(DbInfoInitTask.class);@Autowiredprivate DbInfoRepository dbInfoRepository;@AutowiredDbVersionRepository dbVersionRepository;@Autowiredprivate DbService dbService;@Overridepublic void run(String... arg0) throws Exception {try {//查询是否已经初始化了信息List<InitDbVersionEntity> list = initDbVersionRepository.findAll();if (CollectionUtils.isEmpty(list)) {DBType dbType = dbInfoRepository.getCurrentDialect();importDataBase(dbType); //导入默认数据insertVersionInfo();logger.info("数据库初始化成功!");return;}logger.info("启动不需要重新初始化数据库!");} catch (Exception e) {logger.error("数据库初始化失败!", e);}}/*** 导入初始化默认数据**/private void importDataBase(DBType dbType) throws Exception {String filePath = null;if(DBType.POSTGRESQL.equals(dbType)){filePath = "/sql/postgresql.sql";} else if(DBType.ORACLE.equals(dbType)){filePath = "/sql/oracleb.sql";}if(StringUtils.isNotEmpty(filePath)) {dbService.excuteUpgradeSqlFile(filePath, dbType);}}/*** 初始化结束之后,向表中添加一条信息,以免下一次重复初始化*/private void insertVersionInfo(){DbVersionEntity dbVersionEntity = new DbVersionEntity();dbVersionEntity.setId("1");dbVersionEntity.setAppVersion("2.0");dbVersionEntity.setRemark("数据库初始化控制");dbVersionEntity.setUpdateTime(new Date());dbVersionRepository.save(dbVersionEntity);}
}