forked from p81075629/datagear
[analysis]SqlDataSet添加Freemarker模板格式的sql支持(待完善)
This commit is contained in:
parent
097e0f9951
commit
e2c8677072
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue