gu.sql2java.observer.TriggerGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sql2java-observer Show documentation
Show all versions of sql2java-observer Show documentation
table listener base row observer UDF
package gu.sql2java.observer;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import gu.sql2java.RowMetaData;
import gu.sql2java.exception.RuntimeDaoException;
import gu.sql2java.velocity.Sql2javaClasspathResourceLoader;
import gu.sql2java.velocity.TemplateGenerator;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static gu.sql2java.velocity.TemplateGenerator.generateFile;
import static gu.sql2java.velocity.TemplateGenerator.generateString;
/**
* 触发器SQL语句生成类(当前只支持MySQL),
* 依赖 com.gitee.l0km:sql2java-velocity
* @author guyadong
*
*/
public class TriggerGenerator {
/**
* 保存表对应的字段名列表
*/
private final ConcurrentHashMap> columnNames = new ConcurrentHashMap<>();
/**
* 模板路径的数据名前缀
*/
private String dbprefix = null;
private DatabaseMetaData meta;
private String catalog;
public TriggerGenerator() {
init();
}
public TriggerGenerator(Connection connection) {
this();
setConnection(connection);
}
public TriggerGenerator(DatabaseMetaData meta) {
this();
setDatabaseMetaData(checkNotNull(meta, "meta is null"));
}
/**
* velocity引擎初始化
*/
private void init() {
Properties vprops = new Properties();
vprops.put(Velocity.SET_NULL_ALLOWED, "true");
vprops.put(Velocity.INPUT_ENCODING, "UTF-8");
vprops.put(Velocity.OUTPUT_ENCODING, "UTF-8");
vprops.put(Velocity.VM_LIBRARY, "macros.include.vm");
vprops.put(Velocity.RESOURCE_LOADER, "class");
vprops.put("class.resource.loader.description", "Velocity Classpath Resource Loader");
vprops.put("class.resource.loader.class", Sql2javaClasspathResourceLoader.class.getName());
vprops.put("class.resource.loader.prefix",
"/templates/velocity/ro_notify/includes,/templates/velocity/ro_notify");
TemplateGenerator.init(vprops);
}
/**
* 根据提供的参数生成Velocity引擎工作时的上下文对象{@link VelocityContext}
*
* @param schematable
* 表名(schema+tablename)
* @param columnNames
* 表字段名列表
* @param properties
* 额外的模板变量定义,可为{@code null}
* @return {@link VelocityContext}
*/
private VelocityContext createConext(String schematable, List columnNames, Properties properties) {
String tablename = JDBCUtility.parseTablenme(schematable);
VelocityContext context = new VelocityContext();
context.put("schematable", schematable);
context.put("tablename", tablename);
context.put("columnNames", columnNames);
if (null != properties) {
for (@SuppressWarnings("unchecked")
Enumeration e = (Enumeration) properties.propertyNames(); e.hasMoreElements();) {
String key = e.nextElement();
context.put(key, properties.get(key));
}
}
return context;
}
/**
* 如果{@link #meta}字段不为null,则尝试通过JDBC 接口返回指定表的字段名列表
*
* @param schematable 表名(schema+tablename)
* @return 字段名列表
*/
private List onAbsentByJDBC(String schematable) {
if (null != meta) {
/** 从 schematable 解析出数据库名和表名 */
Pattern p = Pattern.compile("^(\\w+)\\.(\\w+)$");
Matcher m = p.matcher(checkNotNull(schematable, "schematable"));
checkArgument(m.find(), "INVALID schematable format,${schema}.${tablename} required");
String schema = m.group(1);
String tablename = m.group(2);
try {
ImmutableList.Builder builder = ImmutableList.builder();
ResultSet resultSet = checkNotNull(meta, "meta is uninitialized").getColumns(catalog, schema, tablename,
"%");
while (resultSet.next()) {
String column = resultSet.getString("COLUMN_NAME");
builder.add(column);
// System.out.println(tablename + "." + column + " found");
}
resultSet.close();
return builder.build();
} catch (SQLException e) {
throw new RuntimeDaoException(e);
}
}
return null;
}
/**
* 通过 {@link RowMetaData}获取表字段名列表
*
* @param schematable 表名(schema+tablename)
* @return 字段名列表
*/
private List onAbsentByMetadata(String schematable) {
try {
String tablename = JDBCUtility.parseTablenme(schematable);
RowMetaData rowMetaData = RowMetaData.getMetaData(tablename);
return rowMetaData.columnNames;
} catch (Exception e) {
return null;
}
}
/**
* 当 {@link #columnNames}中没有指定表的字段名列表时,调用此方法 子类可以重写些方法,返回指定表的字段名列表
*
* @param schematable 表名(schema+tablename)
* @return 字段名列表
*/
protected List onAbsent(String schematable) {
List names = onAbsentByJDBC(schematable);
if (null != names) {
return names;
}
return onAbsentByMetadata(schematable);
}
/**
* 指定表的字段名列表
*
* @param schematable 表名(schema+tablename)
* @param columnNames
* @return 当前对象
*/
public TriggerGenerator setColumnNames(String schematable, List columnNames) {
this.columnNames.put(checkNotNull(schematable, "schematable is null"),
checkNotNull(columnNames, "columnNames is null"));
return this;
}
private List getColumnNames(String schematable) {
List names = columnNames.get(schematable);
if (null == names) {
synchronized (columnNames) {
names = onAbsent(schematable);
if (names != null) {
columnNames.put(schematable, names);
}
}
}
return checkNotNull(columnNames.get(schematable), "FAIL TO get column names for %s", schematable);
}
/**
* 设置模板路径的数据名前缀,未设置时默认为'mysql', 当前只支持'mysql'
*
* @param dbprefix
* @return 当前对象
*/
public TriggerGenerator setDbprefix(String dbprefix) {
checkArgument(null == dbprefix || "mysql".equals(dbprefix),
"UNSUPPORTE dbprefix %s,for dbprefix,only 'mysql' supported", dbprefix);
this.dbprefix = dbprefix;
return this;
}
/**
* @return 返回模板路径的数据名前缀,默认为'mysql'
*/
private String getDbprefix() {
return MoreObjects.firstNonNull(dbprefix, "mysql");
}
/**
* 指定数据库连接,用于通过JDBC接口获取字段名列表
*
* @param connection
* @return 当前对象
*/
public TriggerGenerator setConnection(Connection connection) {
try {
this.meta = checkNotNull(connection, "connection is null").getMetaData();
} catch (SQLException e) {
throw new RuntimeDaoException(e);
}
return this;
}
/**
* 指定{@link DatabaseMetaData}对象,用于通过JDBC接口获取字段名列表
*
* @param meta
* @return 当前对象
*/
public TriggerGenerator setDatabaseMetaData(DatabaseMetaData meta) {
this.meta = meta;
return this;
}
/**
* 指定 catalog 参数,,用于通过JDBC接口获取字段名列表,默认为null
*
* @param catalog
* @return 当前对象
* @see {@link DatabaseMetaData#getColumns(String, String, String, String)}
*/
public TriggerGenerator setCatalog(String catalog) {
this.catalog = catalog;
return this;
}
/**
* 生成指定表名的创建触发器SQL语句
*
* @param schematable 表名(schema+tablename)
* @param properties
* 额外的模板变量定义,可为{@code null}
* @param install
* 为true生成安装脚本,否则生成删除脚本
*/
public String generateTriggerSQL(String schematable, Properties properties, boolean install) {
// 删除脚本不需要字段名列表
VelocityContext context = createConext(schematable, (install ? getColumnNames(schematable) : null), properties);
return generateString(context, checkNotNull(getDbprefix(), "dbproduct is null")
+ (install ? "/install_ro_notify_trigger.sql.vm" : "/remove_ro_notify_trigger.sql.vm"));
}
/**
* 生成指定表名的创建触发器SQL语句,写入到指定文件
*
* @param schematable 表名(schema+tablename)
* @param filename
* @param properties
* 额外的模板变量定义,可为{@code null}
* @param install
* 为true生成安装脚本,否则生成删除脚本
* @throws IOException
*/
public void generateTriggerSQLFile(String schematable, String filename, Properties properties, boolean install)
throws IOException {
// 删除脚本不需要字段名列表
VelocityContext context = createConext(schematable, (install ? getColumnNames(schematable) : null), properties);
generateFile(context,
checkNotNull(getDbprefix(), "dbproduct is null")
+ (install ? "/install_ro_notify_trigger.sql.vm" : "/remove_ro_notify_trigger.sql.vm"),
filename);
}
}