[persistence]完善类设计,ConversionSqlParamValueMapper添加Hex、Base64转换支持

This commit is contained in:
datagear 2020-03-18 21:21:48 +08:00
parent 39b83478d4
commit 64d0a6087a
10 changed files with 312 additions and 176 deletions

View File

@ -29,11 +29,6 @@
<artifactId>commons-csv</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>

View File

@ -13,9 +13,9 @@ import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.datagear.util.expression.Expression;
import org.datagear.util.expression.ExpressionResolver;
@ -564,7 +564,7 @@ public class DataFormatContext
if (bytes == null)
return null;
return Base64.encodeBase64String(bytes);
return Base64.getEncoder().encodeToString(bytes);
}
/**
@ -596,7 +596,7 @@ public class DataFormatContext
if (value == null || value.isEmpty())
return null;
return Base64.decodeBase64(value);
return Base64.getDecoder().decode(value);
}
/**

View File

@ -28,6 +28,11 @@
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>

View File

@ -41,7 +41,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* 支持类型转换的{@linkplain SqlParamValueMapper}
* <p>
* 对于SQL大对象类型二进制类型此类支持原值是文件路径格式的字符串具体参考{@linkplain FilePathValueResolver}
* 对于SQL大对象类型二进制类型此类通过{@linkplain #getSqlParamValueMapResolver()}支持文件路径Hex编码Base64编码格式的字符串原值
* </p>
* <p>
* 如果{@linkplain #hasExpressionEvaluationContext()}它还支持变量表达式<code>"#{...}"</code>
@ -59,7 +59,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
protected static final SpelExpressionParser DEFAULT_SPEL_EXPRESSION_PARSER = new SpelExpressionParser();
protected static final FilePathValueResolver DEFAULT_FILE_PATH_VALUE_RESOLVER = new FilePathValueResolver();
protected static final SqlParamValueMapResolver DEFAULT_SQL_PARAM_VALUE_MAP_RESOLVER = new SqlParamValueMapResolver();
/** 变量表达式解析器 */
private VariableExpressionResolver variableExpressionResolver = DEFAULT_VARIABLE_EXPRESSION_RESOLVER;
@ -70,7 +70,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
/** 变量表达式计算器 */
private SpelExpressionParser spelExpressionParser = DEFAULT_SPEL_EXPRESSION_PARSER;
private FilePathValueResolver filePathValueResolver = DEFAULT_FILE_PATH_VALUE_RESOLVER;
private SqlParamValueMapResolver sqlParamValueMapResolver = DEFAULT_SQL_PARAM_VALUE_MAP_RESOLVER;
/** 表达式计算上下文 */
private ExpressionEvaluationContext expressionEvaluationContext = null;
@ -110,14 +110,14 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
this.spelExpressionParser = spelExpressionParser;
}
public FilePathValueResolver getFilePathValueResolver()
public SqlParamValueMapResolver getSqlParamValueMapResolver()
{
return filePathValueResolver;
return sqlParamValueMapResolver;
}
public void setFilePathValueResolver(FilePathValueResolver filePathValueResolver)
public void setSqlParamValueMapResolver(SqlParamValueMapResolver sqlParamValueMapResolver)
{
this.filePathValueResolver = filePathValueResolver;
this.sqlParamValueMapResolver = sqlParamValueMapResolver;
}
public boolean hasExpressionEvaluationContext()
@ -196,7 +196,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof Reader)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getReader(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getReader(table, column, (File) value);
else
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, Reader.class);
@ -277,12 +277,15 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof InputStream)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getInputStream(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getInputStream(table, column, (File) value);
else if (value instanceof String)
{
String v = (String) value;
paramValue = getInputStreamIfFilePath(table, column, v);
if (paramValue == null)
paramValue = this.sqlParamValueMapResolver.getBytes(table, column, v);
if (paramValue == null)
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, InputStream.class);
}
@ -339,7 +342,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof InputStream)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getReader(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getReader(table, column, (File) value);
else
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, Clob.class);
@ -355,12 +358,15 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof InputStream)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getInputStream(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getInputStream(table, column, (File) value);
else if (value instanceof String)
{
String v = (String) value;
paramValue = getInputStreamIfFilePath(table, column, v);
if (paramValue == null)
paramValue = this.sqlParamValueMapResolver.getBytes(table, column, v);
if (paramValue == null)
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, Blob.class);
}
@ -394,7 +400,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof Reader)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getReader(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getReader(table, column, (File) value);
else
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, Reader.class);
@ -418,7 +424,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof InputStream)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getReader(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getReader(table, column, (File) value);
else
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, NClob.class);
@ -442,7 +448,7 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
else if (value instanceof InputStream)
paramValue = value;
else if (value instanceof File)
paramValue = this.filePathValueResolver.getReader(table, column, (File) value);
paramValue = this.sqlParamValueMapResolver.getReader(table, column, (File) value);
else
sqlParamValue = convertToSqlParamValueExtWrap(cn, table, column, value, SQLXML.class);
@ -509,8 +515,8 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
*/
protected Reader getReaderIfFilePath(Table table, Column column, String value)
{
File file = this.filePathValueResolver.getFileValue(table, column, value);
return (file == null ? null : this.filePathValueResolver.getReader(table, column, file));
File file = this.sqlParamValueMapResolver.getFile(table, column, value);
return (file == null ? null : this.sqlParamValueMapResolver.getReader(table, column, file));
}
/**
@ -523,8 +529,8 @@ public class ConversionSqlParamValueMapper extends AbstractSqlParamValueMapper
*/
protected InputStream getInputStreamIfFilePath(Table table, Column column, String value)
{
File file = this.filePathValueResolver.getFileValue(table, column, value);
return (file == null ? null : this.filePathValueResolver.getInputStream(table, column, file));
File file = this.sqlParamValueMapResolver.getFile(table, column, value);
return (file == null ? null : this.sqlParamValueMapResolver.getInputStream(table, column, file));
}
/**

View File

@ -73,6 +73,7 @@ public class DefaultPersistenceManager extends PersistenceSupport implements Per
{
dialect = getDialect(cn, dialect);
// 用于避免SQL参数转换中出现异常导致已转换的资源无法释放
ReleasableRegistry releasableRegistry = createReleasableRegistry();
Sql sql = Sql.valueOf().sql("INSERT INTO ").sql(quote(dialect, table.getName())).sql(" (").delimit(",");
@ -128,6 +129,7 @@ public class DefaultPersistenceManager extends PersistenceSupport implements Per
{
dialect = getDialect(cn, dialect);
// 用于避免SQL参数转换中出现异常导致已转换的资源无法释放
ReleasableRegistry releasableRegistry = createReleasableRegistry();
Sql sql = Sql.valueOf().sql("UPDATE ").sql(quote(dialect, table.getName())).sql(" SET ").delimit(",");
@ -180,6 +182,7 @@ public class DefaultPersistenceManager extends PersistenceSupport implements Per
{
dialect = getDialect(cn, dialect);
// 用于避免SQL参数转换中出现异常导致已转换的资源无法释放
ReleasableRegistry releasableRegistry = createReleasableRegistry();
Sql sql = Sql.valueOf().sql("DELETE FROM ").sql(quote(dialect, table.getName())).sql(" WHERE ")
@ -264,6 +267,7 @@ public class DefaultPersistenceManager extends PersistenceSupport implements Per
{
dialect = getDialect(cn, dialect);
// 用于避免SQL参数转换中出现异常导致已转换的资源无法释放
ReleasableRegistry releasableRegistry = createReleasableRegistry();
Sql sql = Sql.valueOf().sql("SELECT * FROM ").sql(quote(dialect, table.getName())).sql(" WHERE ")

View File

@ -12,7 +12,7 @@ import org.datagear.persistence.SqlParamValueMapperException;
import org.datagear.util.FileUtil;
/**
* 指定目录的{@linkplain FilePathValueResolver}
* 指定目录的{@linkplain SqlParamValueMapResolver}
* <p>
* 它限定特定目录下的相对文件路径
* </p>
@ -20,17 +20,17 @@ import org.datagear.util.FileUtil;
* @author datagear@163.com
*
*/
public class DirectoryFilePathValueResolver extends FilePathValueResolver
public class DirectorySqlParamValueMapResolver extends SqlParamValueMapResolver
{
/** 文件根目录 */
private File directory;
public DirectoryFilePathValueResolver()
public DirectorySqlParamValueMapResolver()
{
super();
}
public DirectoryFilePathValueResolver(File directory)
public DirectorySqlParamValueMapResolver(File directory)
{
super();
this.directory = directory;
@ -47,14 +47,14 @@ public class DirectoryFilePathValueResolver extends FilePathValueResolver
}
@Override
public File getFileValue(Table table, Column column, String filePathValue) throws SqlParamValueMapperException
public File getFile(Table table, Column column, String value) throws SqlParamValueMapperException
{
if (!isFilePathValue(filePathValue))
if (!isFile(value))
return null;
filePathValue = getFilePath(filePathValue);
value = getFilePath(value);
File file = FileUtil.getFile(this.directory, filePathValue);
File file = FileUtil.getFile(this.directory, value);
return (file.exists() ? file : null);
}

View File

@ -1,139 +0,0 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.persistence.support;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import org.datagear.meta.Column;
import org.datagear.meta.Table;
import org.datagear.persistence.SqlParamValueMapperException;
import org.datagear.util.IOUtil;
import org.datagear.util.StringUtil;
/**
* 文件路径列值处理器
* <p>
* 此类用于为{@linkplain ConversionSqlParamValueMapper}支持获取特定格式的文件路径字符串所表示的文件及其输入流
* </p>
*
* @author datagear@163.com
*
*/
public class FilePathValueResolver
{
/** 文件路径值前缀 */
public static final String FILE_PATH_VALUE_PREFIX = "file:";
/** 文件的字符集 */
private String fileCharset;
public FilePathValueResolver()
{
super();
}
public boolean hasFileCharset()
{
return !StringUtil.isEmpty(this.fileCharset);
}
public String getFileCharset()
{
return fileCharset;
}
public void setFileCharset(String fileCharset)
{
this.fileCharset = fileCharset;
}
/**
* 获取指定文件路径值的文件
*
* @param table
* @param column
* @param filePathValue
* @return 返回{@code null}表示不是文件路径值
* @throws SqlParamValueMapperException
*/
public File getFileValue(Table table, Column column, String filePathValue) throws SqlParamValueMapperException
{
if (!isFilePathValue(filePathValue))
return null;
File file = new File(getFilePath(filePathValue));
return (file.exists() ? file : null);
}
/**
* 获取文件值输入流
*
* @param table
* @param column
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public InputStream getInputStream(Table table, Column column, File value) throws SqlParamValueMapperException
{
try
{
return IOUtil.getInputStream(value);
}
catch (FileNotFoundException e)
{
throw new SqlParamValueMapperException(e);
}
}
/**
* 获取文件值输入流
*
* @param table
* @param column
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public Reader getReader(Table table, Column column, File value) throws SqlParamValueMapperException
{
try
{
return IOUtil.getReader(value, this.fileCharset);
}
catch (IOException e)
{
throw new SqlParamValueMapperException(e);
}
}
/**
* 给定值是否是文件路径值
*
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public boolean isFilePathValue(String value) throws SqlParamValueMapperException
{
return (value != null && value.startsWith(FILE_PATH_VALUE_PREFIX));
}
/**
* 获取以{@linkplain #FILE_PATH_VALUE_PREFIX}开头文件路径值的路径
*
* @param filePathValue
* @return
*/
protected String getFilePath(String filePathValue)
{
return filePathValue.substring(FILE_PATH_VALUE_PREFIX.length());
}
}

View File

@ -23,11 +23,17 @@ import org.datagear.util.IOUtil;
*/
public class ReleasableRegistry
{
private List<Object> releasables = new ArrayList<>();
private List<Object> releasables;
public ReleasableRegistry()
{
this(2);
}
public ReleasableRegistry(int initialCapacity)
{
super();
this.releasables = new ArrayList<Object>(initialCapacity);
}
public List<Object> getReleasables()

View File

@ -0,0 +1,242 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.persistence.support;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Base64;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.datagear.meta.Column;
import org.datagear.meta.Table;
import org.datagear.persistence.SqlParamValueMapperException;
import org.datagear.util.IOUtil;
import org.datagear.util.StringUtil;
/**
* SQL参数值处理器
* <p>
* 此类用于为{@linkplain ConversionSqlParamValueMapper}提供字符串特殊格式文件输入流处理相关支持
* </p>
*
* @author datagear@163.com
*
*/
public class SqlParamValueMapResolver
{
/** 文件路径值前缀 */
public static final String PREFIX_FILE_PATH = "file:";
/** Hex值前缀 */
public static final String PREFIX_HEX = "hex:";
/** Base64值前缀 */
public static final String PREFIX_BASE64 = "base64:";
/** 文件的字符集 */
private String fileCharset;
public SqlParamValueMapResolver()
{
super();
}
public boolean hasFileCharset()
{
return !StringUtil.isEmpty(this.fileCharset);
}
public String getFileCharset()
{
return fileCharset;
}
public void setFileCharset(String fileCharset)
{
this.fileCharset = fileCharset;
}
/**
* 获取指定字符串所表示的字节数组
*
* @param table
* @param column
* @param value
* @return 返回{@code null}表示不是字节数组
* @throws SqlParamValueMapperException
*/
public byte[] getBytes(Table table, Column column, String value) throws SqlParamValueMapperException
{
if (!isBytes(value))
return null;
return toBytes(value);
}
/**
* 获取指定字符串所表示的文件
*
* @param table
* @param column
* @param value
* @return 返回{@code null}表示不是文件
* @throws SqlParamValueMapperException
*/
public File getFile(Table table, Column column, String value) throws SqlParamValueMapperException
{
if (!isFile(value))
return null;
File file = new File(getFilePath(value));
return (file.exists() ? file : null);
}
/**
* 获取文件输入流
*
* @param table
* @param column
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public InputStream getInputStream(Table table, Column column, File value) throws SqlParamValueMapperException
{
try
{
return IOUtil.getInputStream(value);
}
catch (FileNotFoundException e)
{
throw new SqlParamValueMapperException(e);
}
}
/**
* 获取文件输入流
*
* @param table
* @param column
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public Reader getReader(Table table, Column column, File value) throws SqlParamValueMapperException
{
try
{
return IOUtil.getReader(value, this.fileCharset);
}
catch (IOException e)
{
throw new SqlParamValueMapperException(e);
}
}
/**
* 给定字符串是否表示文件
*
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public boolean isFile(String value) throws SqlParamValueMapperException
{
return (value != null && value.startsWith(PREFIX_FILE_PATH));
}
/**
* 给定字符串是否表示字节数组
*
* @param value
* @return
* @throws SqlParamValueMapperException
*/
public boolean isBytes(String value) throws SqlParamValueMapperException
{
if (value == null)
return false;
return (value.startsWith(PREFIX_HEX) || value.startsWith(PREFIX_BASE64));
}
/**
* 获取{@linkplain #isFile(String)}字符串的文件路径
*
* @param filePathValue
* @return
*/
protected String getFilePath(String filePathValue)
{
return filePathValue.substring(PREFIX_FILE_PATH.length());
}
/**
* {@linkplain #isBytes(String)}字符串转换为字节数组
*
* @param value
* @return
* @throws SqlParamValueMapperException
*/
protected byte[] toBytes(String value) throws SqlParamValueMapperException
{
byte[] bytes = null;
try
{
if (value.startsWith(PREFIX_HEX))
bytes = convertToBytesForHex(value.substring(PREFIX_HEX.length()));
else if (value.startsWith(PREFIX_BASE64))
bytes = convertToBytesForBase64(value.substring(PREFIX_BASE64.length()));
return bytes;
}
catch(SqlParamValueMapperException e)
{
throw e;
}
catch(Throwable t)
{
throw new SqlParamValueMapperException(t);
}
}
/**
* 将Hex编码的字符串转换为字节数组
*
* @param value
* @return
* @throws DecoderException
*/
protected byte[] convertToBytesForHex(String value) throws DecoderException
{
if (value == null || value.isEmpty())
return null;
if (value.startsWith("0x") || value.startsWith("0X"))
value = value.substring(2);
return Hex.decodeHex(value);
}
/**
* 将Base64编码的字符串转换为字节数组
*
* @param value
* @return
*/
protected byte[] convertToBytesForBase64(String value)
{
if (value == null || value.isEmpty())
return null;
return Base64.getDecoder().decode(value);
}
}

View File

@ -4,9 +4,11 @@
package org.datagear.persistence.support;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.InputStream;
import java.sql.Connection;
import java.util.List;
@ -16,6 +18,7 @@ import org.datagear.persistence.DialectSource;
import org.datagear.persistence.PagingData;
import org.datagear.persistence.PagingQuery;
import org.datagear.persistence.Row;
import org.datagear.util.IOUtil;
import org.datagear.util.JdbcUtil;
import org.datagear.util.test.DBTestSupport;
import org.junit.After;
@ -57,25 +60,29 @@ public class DefaultPersistenceManagerTest extends DBTestSupport
}
@Test
public void insertTest()
public void insertTest() throws Exception
{
Table table = this.genericDBMetaResolver.getTable(this.connection, "T_ACCOUNT");
int id = 999999999;
String name = "NAME-FOR-TEST";
String INTRODUCTION = "INTRODUCTION-for-test";
Row row = new Row();
row.put("ID", id);
row.put("NAME", name);
row.put("HEAD_IMG", "hex:0x09");
row.put("INTRODUCTION", INTRODUCTION);
try
{
this.defaultPersistenceManager.insert(connection, table, row);
this.defaultPersistenceManager.insert(connection, null, table, row, new ConversionSqlParamValueMapper());
Row getRow = this.defaultPersistenceManager.get(connection, table, row);
assertEquals(id, ((Number) getRow.get("ID")).intValue());
assertEquals(name, getRow.get("NAME"));
assertArrayEquals(new byte[] { 0x09 }, toBytes(getRow.get("HEAD_IMG")));
}
finally
{
@ -94,4 +101,14 @@ public class DefaultPersistenceManagerTest extends DBTestSupport
assertTrue(rows.size() <= 1);
}
protected byte[] toBytes(Object o) throws Exception
{
if (o instanceof byte[])
return (byte[]) o;
else if (o instanceof InputStream)
return IOUtil.getBytes((InputStream) o);
else
throw new UnsupportedOperationException();
}
}