[analysis]SqlDataSet添加Freemarker模板格式的sql支持(待完善)

This commit is contained in:
interestinglife 2020-05-06 00:21:42 +08:00
parent 097e0f9951
commit e2c8677072
6 changed files with 411 additions and 26 deletions

View File

@ -34,6 +34,11 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -22,6 +22,7 @@ import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.support.ParameterSqlResolver.ParameterSql;
import org.datagear.util.JdbcUtil;
import org.datagear.util.QueryResultSet;
import org.datagear.util.Sql;
import org.datagear.util.resource.ConnectionFactory;
/**
@ -46,6 +47,8 @@ public class SqlDataSet extends AbstractDataSet
private String sql;
private SqlDataSetSqlResolver sqlDataSetSqlResolver;
public SqlDataSet()
{
super();
@ -79,6 +82,16 @@ public class SqlDataSet extends AbstractDataSet
this.sql = sql;
}
public SqlDataSetSqlResolver getSqlDataSetSqlResolver()
{
return sqlDataSetSqlResolver;
}
public void setSqlDataSetSqlResolver(SqlDataSetSqlResolver sqlDataSetSqlResolver)
{
this.sqlDataSetSqlResolver = sqlDataSetSqlResolver;
}
@Override
public DataSetResult getResult(Map<String, ?> paramValues) throws DataSetException
{
@ -94,9 +107,16 @@ public class SqlDataSet extends AbstractDataSet
throw new SqlDataSetConnectionException(e);
}
String sql = this.sql;
if (this.sqlDataSetSqlResolver != null)
{
Sql sqlObj = this.sqlDataSetSqlResolver.resolve(this, paramValues);
sql = sqlObj.getSqlValue();
}
try
{
return getResult(cn, this.sql, paramValues);
return getResult(cn, sql, paramValues);
}
finally
{
@ -150,7 +170,7 @@ public class SqlDataSet extends AbstractDataSet
try
{
qrs = getSqlDataSetSupport().buildQueryResultSet(cn, sql, dataSetParams, paramValues);
return toDataSet(cn, qrs.getResultSet());
return toDataSetResult(cn, qrs.getResultSet());
}
catch (SQLException e)
{
@ -171,7 +191,7 @@ public class SqlDataSet extends AbstractDataSet
* @return
* @throws SQLException
*/
public DataSetResult toDataSet(Connection cn, ResultSet rs) throws SQLException
public DataSetResult toDataSetResult(Connection cn, ResultSet rs) throws SQLException
{
List<Map<String, ?>> datas = getSqlDataSetSupport().resolveDatas(cn, rs, getProperties());
MapDataSetResult result = new MapDataSetResult(datas);

View File

@ -0,0 +1,314 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.datagear.util.Sql;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* Freemarker SQL模板{@linkplain SqlDataSetSqlResolver}
* <p>
* 它将{@linkplain SqlDataSet#getSql()}作为Freemarker模板处理
* </p>
*
* @author datagear@163.com
*
*/
public class SqlDataSetFmkSqlResolver implements SqlDataSetSqlResolver
{
private SqlDataSetTemplateLoader dataSetTemplateLoader;
private Configuration configuration;
public SqlDataSetFmkSqlResolver()
{
super();
int cacheCapacity = 500;
this.dataSetTemplateLoader = new SqlDataSetTemplateLoader();
this.dataSetTemplateLoader.setCapacity(cacheCapacity);
this.configuration = new Configuration(Configuration.VERSION_2_3_30);
this.configuration.setTemplateLoader(this.dataSetTemplateLoader);
this.configuration.setCacheStorage(new freemarker.cache.MruCacheStorage(0, cacheCapacity));
}
public SqlDataSetTemplateLoader getDataSetTemplateLoader()
{
return dataSetTemplateLoader;
}
public void setDataSetTemplateLoader(SqlDataSetTemplateLoader dataSetTemplateLoader)
{
this.dataSetTemplateLoader = dataSetTemplateLoader;
if (this.configuration != null)
this.configuration.setTemplateLoader(this.dataSetTemplateLoader);
}
public Configuration getConfiguration()
{
return configuration;
}
public void setConfiguration(Configuration configuration)
{
this.configuration = configuration;
if (this.dataSetTemplateLoader != null)
this.configuration.setTemplateLoader(this.dataSetTemplateLoader);
}
@Override
public Sql resolve(SqlDataSet sqlDataSet, Map<String, ?> dataSetParamValues) throws SqlDataSetSqlResolverException
{
this.dataSetTemplateLoader.updateTemplate(sqlDataSet);
String sql = null;
try
{
Template template = this.configuration.getTemplate(this.dataSetTemplateLoader.getTemplateName(sqlDataSet));
StringWriter out = new StringWriter();
template.process(dataSetParamValues, out);
sql = out.toString();
}
catch(IOException e)
{
throw new SqlDataSetSqlResolverException(e);
}
catch(TemplateException e)
{
throw new SqlDataSetSqlResolverException(e);
}
return Sql.valueOf(sql);
}
public static class SqlDataSetTemplateLoader implements TemplateLoader
{
private Map<String, SqlDataSetTemplateSource> sqlDataSetTemplateSources = new HashMap<String, SqlDataSetTemplateSource>();
private int capacity = 500;
private int expiredSeconds = 60 * 10;
private ReadWriteLock _lock = new ReentrantReadWriteLock();
public SqlDataSetTemplateLoader()
{
super();
}
public int getCapacity()
{
return capacity;
}
public void setCapacity(int capacity)
{
this.capacity = capacity;
}
public int getExpiredSeconds()
{
return expiredSeconds;
}
public void setExpiredSeconds(int expiredSeconds)
{
this.expiredSeconds = expiredSeconds;
}
public String getTemplateName(SqlDataSet sqlDataSet)
{
return sqlDataSet.getId();
}
/**
* {@linkplain SqlDataSet#getSql()}更新为Freemarker模板
*
* @param sqlDataSet
* @return true 更新成功false 未更新因为{@linkplain SqlDataSet#getSql()}自上次以来未修改
*/
public boolean updateTemplate(SqlDataSet sqlDataSet)
{
String templateName = getTemplateName(sqlDataSet);
Lock readLock = this._lock.readLock();
try
{
readLock.lock();
SqlDataSetTemplateSource sts = this.sqlDataSetTemplateSources.get(templateName);
if (sts != null && sts.getSql().equals(sqlDataSet.getSql()))
return false;
}
finally
{
readLock.unlock();
}
Lock writeLock = this._lock.writeLock();
try
{
writeLock.lock();
long currentTime = System.currentTimeMillis();
long expiredTime = currentTime - this.expiredSeconds * 1000;
SqlDataSetTemplateSource sts = new SqlDataSetTemplateSource(sqlDataSet, currentTime);
this.sqlDataSetTemplateSources.put(templateName, sts);
if (this.sqlDataSetTemplateSources.size() >= this.capacity)
{
Collection<SqlDataSetTemplateSource> stss = this.sqlDataSetTemplateSources.values();
List<SqlDataSetTemplateSource> list = new ArrayList<SqlDataSetTemplateSource>(stss.size());
list.addAll(stss);
Collections.sort(list, MRU_COMPARATOR);
this.sqlDataSetTemplateSources.clear();
int count = 0;
for (SqlDataSetTemplateSource ele : list)
{
if(count > this.capacity || ele.getLastModified() < expiredTime)
break;
this.sqlDataSetTemplateSources.put(templateName, ele);
}
}
return true;
}
finally
{
writeLock.unlock();
}
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException
{
}
@Override
public Object findTemplateSource(String name) throws IOException
{
Lock readLock = this._lock.readLock();
try
{
readLock.lock();
return this.sqlDataSetTemplateSources.get(name);
}
finally
{
readLock.unlock();
}
}
@Override
public long getLastModified(Object templateSource)
{
return ((SqlDataSetTemplateSource) templateSource).getLastModified();
}
@Override
public Reader getReader(Object templateSource, String encoding) throws IOException
{
return new StringReader(((SqlDataSetTemplateSource) templateSource).getSql());
}
protected static class SqlDataSetTemplateSource
{
private final String name;
private final String sql;
private final long lastModified;
public SqlDataSetTemplateSource(SqlDataSet sqlDataSet, long lastModified)
{
super();
this.name = sqlDataSet.getId();
this.sql = sqlDataSet.getSql();
this.lastModified = lastModified;
}
public String getName()
{
return name;
}
public String getSql()
{
return sql;
}
public long getLastModified()
{
return lastModified;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SqlDataSetTemplateSource other = (SqlDataSetTemplateSource) obj;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}
private static final Comparator<SqlDataSetTemplateSource> MRU_COMPARATOR = new Comparator<SqlDataSetTemplateSource>()
{
@Override
public int compare(SqlDataSetTemplateSource o1, SqlDataSetTemplateSource o2)
{
return (0 - (int) (o1.getLastModified() - o2.getLastModified()));
}
};
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.util.Map;
import org.datagear.util.Sql;
/**
* {@linkplain SqlDataSet}{@linkplain Sql}解析器
*
* @author datagear@163.com
*
*/
public interface SqlDataSetSqlResolver
{
/**
* 解析
*
* @param sqlDataSet
* @param dataSetParamValues
* @return
* @throws SqlDataSetSqlResolverException
*/
Sql resolve(SqlDataSet sqlDataSet, Map<String, ?> dataSetParamValues) throws SqlDataSetSqlResolverException;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import org.datagear.analysis.DataSetException;
/**
* {@linkplain SqlDataSetSqlResolver}异常
*
* @author datagear@163.com
*
*/
public class SqlDataSetSqlResolverException extends DataSetException
{
private static final long serialVersionUID = 1L;
public SqlDataSetSqlResolverException()
{
super();
}
public SqlDataSetSqlResolverException(String message)
{
super(message);
}
public SqlDataSetSqlResolverException(Throwable cause)
{
super(cause);
}
public SqlDataSetSqlResolverException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -59,7 +59,7 @@ public class SqlDataSetTest extends DBTestSupport
}
}
String sql = "SELECT ID, NAME FROM T_ACCOUNT WHERE ID = #{id} AND NAME != #{name}";
String sql = "SELECT ID, NAME FROM T_ACCOUNT <#if id??>WHERE ID = ${id} AND NAME != '${name}'</#if>";
List<DataSetProperty> dataSetProperties = Arrays.asList(new DataSetProperty("ID", DataType.INTEGER),
new DataSetProperty("NAME", DataType.STRING));
@ -68,10 +68,11 @@ public class SqlDataSetTest extends DBTestSupport
SqlDataSet sqlDataSet = new SqlDataSet("1", "1", dataSetProperties, connectionFactory, sql);
sqlDataSet.setParams(dataSetParams);
sqlDataSet.setSqlDataSetSqlResolver(new SqlDataSetFmkSqlResolver());
{
Map<String, Object> dataSetParamValues = new HashMap<String, Object>();
dataSetParamValues.put("id", recordId);
dataSetParamValues.put("id", recordId + "");
dataSetParamValues.put("name", "name-for-test");
DataSetResult dataSetResult = sqlDataSet.getResult(dataSetParamValues);
@ -89,27 +90,6 @@ public class SqlDataSetTest extends DBTestSupport
Assert.assertEquals(recordName, row.get("NAME"));
}
}
{
Map<String, Object> dataSetParamValues = new HashMap<String, Object>();
dataSetParamValues.put("id", recordId);
dataSetParamValues.put("name", "${(SELECT 'name-for-test' FROM T_ACCOUNT WHERE ID=" + recordId + ")}");
DataSetResult dataSetResult = sqlDataSet.getResult(dataSetParamValues);
@SuppressWarnings("unchecked")
List<Map<String, ?>> datas = (List<Map<String, ?>>) dataSetResult.getDatas();
Assert.assertEquals(1, datas.size());
{
Map<String, ?> row = datas.get(0);
Assert.assertEquals(2, row.size());
Assert.assertEquals(Long.toString(recordId), row.get("ID").toString());
Assert.assertEquals(recordName, row.get("NAME"));
}
}
}
finally
{