All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.sql2java.BaseEmbeddedInitializer Maven / Gradle / Ivy

There is a newer version: 5.2.0
Show newest version
package gu.sql2java;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Throwables;
import com.google.common.collect.Maps;

import gu.sql2java.Constant.JdbcProperty;

import static com.google.common.base.Throwables.throwIfInstanceOf;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static gu.sql2java.Constant.JdbcProperty.*;
import static gu.sql2java.SimpleLog.log;


/**
 * 嵌入式数据库初始化管理对象基类
* @author guyadong * */ public abstract class BaseEmbeddedInitializer implements Closeable{ static final int DEFAULT_BACKUP_INTERVAL = 300; protected final boolean runInMemory; protected int backupIntervalSeconds = DEFAULT_BACKUP_INTERVAL; /** * 数据库所在的文件夹 */ protected final File dbroot; /** * 数据库位置(文件或文件夹) */ protected final File db; protected final Properties dbprops = new Properties(); /** * 数据库持久化侦听器容器 */ private final InterfaceContainer backuphooks = new InterfaceContainer(BackupHook.class); private final URL createSql; private long lastBackupBeginTimeMills = 0L; private long lastBackupEndTimeMills = 0L; private volatile boolean opened = false; /** * 构造方法 * @param db 数据库位置 * @param createSql 数据库建表语句SQL文件位置,数据库存在时可为{@code null},当数据库文件不存在时就用到. * @param runInMemory */ protected BaseEmbeddedInitializer(File db, URL createSql, boolean runInMemory) { try { this.db = checkNotNull(db,"db is null").getCanonicalFile(); } catch (IOException e) { throw new IllegalArgumentException(e); } checkArgument(this.db.exists() || null != createSql ,"createSql must not be null if %s not exists",this.db ); this.createSql = createSql; this.dbroot = this.db.getParentFile() ; dbroot.mkdirs(); this.runInMemory = runInMemory; } /** * 将数据库回写到磁盘指定的目录 */ protected abstract void doPersist() ; /** * 写入数据连接参数 * @param dbprops */ protected abstract void writeDbProps(Properties dbprops); /** * 检查已经存在的数据库是否可用,不可用抛出{@link EmbeddedInitException}异常 * @param db 数据库位置 * @throws EmbeddedInitException 初始化异常 */ protected abstract void checkExistsDatabse(File db) throws EmbeddedInitException; /** * 子类可重写此方法实现内存运行时从磁盘文件恢复数据到内存 */ protected void doInitMemory(){} /** * 子类可重写此方法执行对SQL语句的归一化处理 * @param runner * @return always runner */ protected ScriptRunner normalize(ScriptRunner runner){ return runner; } /** * 子类可重写此方法返回需要附加执行的SQL语句(每行一条语句),如果没有返回空表,不可返回{@code null} * @param runner * @throws SQLException */ protected List afterCreateTable(ScriptRunner runner) throws SQLException{ return Collections.emptyList(); } /** * 初始化数据库 * @return 当前对象 * @throws EmbeddedInitException */ public synchronized BaseEmbeddedInitializer init() throws EmbeddedInitException { if(!opened){ try{ log("database location:[{}]", this.db.getAbsolutePath()); this.createManagerInstance(); if (this.db.exists()) { checkExistsDatabse(db); if(runInMemory){ doInitMemory(); } } else { // 如果没找到数据库文件,就新建数据库 log("Initializing database {}...", db); ScriptRunner runner = new ScriptRunner(false, true) .setClearComment(true) .setAlias(dbprops.getProperty(JdbcProperty.ALIAS.key)); normalize(runner).runScript(createSql.openStream()); List sqls = afterCreateTable(runner); if(!sqls.isEmpty()){ runner.runScript(sqls); } } if(runInMemory){ startBackuper(); } }catch (Exception e) { throwIfInstanceOf(e, EmbeddedInitException.class); throw new EmbeddedInitException(e); } opened = true; } return this; } @Override public synchronized void close() { if(opened){ persist(); opened = false; } } public InterfaceContainer getBackuphookContainer() { return backuphooks; } /** * @return 备份间隔时间(秒) */ public int getBackupIntervalSeconds() { return backupIntervalSeconds; } /** * 设置备份间隔时间(秒) * @param backupIntervalSeconds 小于等于0忽略 * @return 当前对象 */ public BaseEmbeddedInitializer setBackupIntervalSeconds(int backupIntervalSeconds) { if(backupIntervalSeconds > 0){ this.backupIntervalSeconds = backupIntervalSeconds; } return this; } public BaseEmbeddedInitializer addProperties(Properties properties) { if(null != properties ){ for( String propertyName : properties.stringPropertyNames()){ dbprops.setProperty(propertyName, properties.getProperty(propertyName)); } } return this; } /** * 将数据库回写到磁盘指定的目录 */ private synchronized void persist() { lastBackupBeginTimeMills = System.currentTimeMillis(); try { this.backuphooks.container.onPersistDB(); if (this.runInMemory) { log("wirte in-memory database to {}", db.getAbsolutePath()); doPersist(); log("write back success"); } } catch (Exception e) { log(e.getMessage(), e); } finally { lastBackupEndTimeMills = System.currentTimeMillis(); } } /** * 根据数据库配置参数创建数据库连接实例 */ private void createManagerInstance() { writeDbProps(dbprops); checkState(null != dbprops.getProperty(JDBC_URL.key),"%s not defined",JDBC_URL.key); // 创建数据库连接实例 Managers.createInstance(dbprops); } /** * 启动备份线程 定期将内存中的数据回写到数据库进行备份 当derby数据库在内存运行时,还要进行derby数据库备份 * * @see #persist() */ private void startBackuper() { Thread t = new Thread("edb-backuper") { @Override public void run() { long intervalMills = TimeUnit.MILLISECONDS.convert(backupIntervalSeconds, TimeUnit.SECONDS); log("backup thread start ,interval(s) :{}", backupIntervalSeconds); try { //通过线程中断状态来决定是否结束循环 while (true) { Thread.sleep(intervalMills); // 通过lastBackupBeginTimeMills是否小于 lastBackupEndTimeMills,来判断止次备份是否结束 if (lastBackupBeginTimeMills < lastBackupEndTimeMills) {// 正常备份 if ((System.currentTimeMillis() - lastBackupEndTimeMills) >= intervalMills) persist(); } else if (0 == lastBackupBeginTimeMills && lastBackupBeginTimeMills == lastBackupEndTimeMills)// 第一次备份 persist(); else // 上次备份还没结束 log("SKIP backup because of too short interval"); } } catch (InterruptedException e) { } finally { log("{} end (结束)",Thread.currentThread().getName()); } } }; t.setDaemon(true); t.start(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((db == null) ? 0 : db.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof BaseEmbeddedInitializer)) return false; BaseEmbeddedInitializer other = (BaseEmbeddedInitializer) obj; if (db == null) { if (other.db != null) return false; } else if (!db.equals(other.db)) return false; return true; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName() + " [runInMemory="); builder.append(runInMemory); builder.append(", opened="); builder.append(opened); builder.append(", db="); builder.append(db); builder.append(", dbprops="); builder.append(dbprops); builder.append(", createSql="); builder.append(createSql); builder.append("]"); return builder.toString(); } private static final Map dbs = Maps.newHashMap(); /** * 数据库初始化
* 反射方式实现构造子类对象并执行初始化({@link #init()}),如果之前已经创建过{@code db}指定的数据库实例,则返回已经创建的实例 * @param target 要构造的子类 * @param db 数据文件位置 * @param createSql 数据库建表语句(SQL)位置 * @param runInMemory 为{@code true}以内存方式运行 * @param properties 附加的配置参数 * @return T instance */ @SuppressWarnings("unchecked") protected static synchronized T init(Classtarget, File db, URL createSql, boolean runInMemory, Properties properties){ try { if(dbs.containsKey(db)){ return (T) dbs.get(db); } final T initializer = target.getConstructor(File.class, URL.class, boolean.class).newInstance(db, createSql, runInMemory); initializer.addProperties(properties).init(); // JVM结束时自动执行关闭数据库 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { initializer.close(); } }, initializer.getClass().getSimpleName() + "-closer")); return initializer; } catch (InvocationTargetException e) { Throwables.throwIfUnchecked(e.getTargetException()); throw new RuntimeException(e.getTargetException()); } catch ( Exception e) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } } /** * 数据库初始化 * @param db 数据文件位置(File) * @param createSqlURL 数据库建表语句(SQL)位置(URL) * @param runInMemory 为{@code true}以内存方式运行 * @param properties 附加的配置参数 * @return T instance */ protected static T init(Classtarget, String db, String createSqlURL, boolean runInMemory, Properties properties){ try { return init(target, new File(db), new URL(createSqlURL), runInMemory, properties); } catch (MalformedURLException e) { throw new RuntimeException(e); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy