Model添加NotEditable特性,用于支持只读数据库,并修改相关逻辑

This commit is contained in:
datagear 2019-10-11 18:50:04 +08:00
parent 1bce008153
commit 91be070466
6 changed files with 136 additions and 21 deletions

View File

@ -5,6 +5,7 @@
package org.datagear.dbmodel;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
@ -68,6 +69,8 @@ import org.datagear.persistence.features.RelationPoint;
import org.datagear.persistence.features.TableName;
import org.datagear.persistence.mapper.Mapper;
import org.datagear.persistence.mapper.MapperResolver;
import org.datagear.util.JDBCCompatiblity;
import org.datagear.util.JdbcUtil;
import org.springframework.core.convert.ConversionService;
/**
@ -226,6 +229,20 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
public Model resolve(Connection cn, ModelManager globalModelManager, ModelManager localModelManager, String table)
throws DatabaseModelResolverException
{
@JDBCCompatiblity("如果cn为readonly某些驱动程序的DatabaseMetaData.isReadOnly()也将为true比如Postgresql JDBC 42.2.5"
+ "这会导致解析Model的NotEditable不正确因此这里设为false以保证解析正确")
boolean readonly = false;
try
{
readonly = cn.isReadOnly();
}
catch (SQLException e)
{
readonly = true;
}
if (readonly)
JdbcUtil.setReadonlyIfSupports(cn, false);
String modelName = this.modelNameResolver.resolve(table);
Model model = doResolve(cn, globalModelManager, localModelManager, table, modelName);
@ -2196,6 +2213,7 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
{
resolveModelFeatureTableName(cn, globalModelManager, localModelManager, modelBuilder, entireTableInfo);
resolveModelFeatureDescLabel(cn, globalModelManager, localModelManager, modelBuilder, entireTableInfo);
resolveModelFeatureNotEditable(cn, globalModelManager, localModelManager, modelBuilder, entireTableInfo);
}
/**
@ -2237,6 +2255,36 @@ public abstract class AbstractDevotedDatabaseModelResolver implements DevotedDat
addDescLabelFeature(modelBuilder, new Label(comment));
}
/**
* 解析{@linkplain Model}{@linkplain NotEditable}特性
*
* @param cn
* @param globalModelManager
* @param localModelManager
* @param modelBuilder
* @param entireTableInfo
* @throws DatabaseModelResolverException
*
*/
protected void resolveModelFeatureNotEditable(Connection cn, ModelManager globalModelManager,
ModelManager localModelManager, ModelBuilder modelBuilder, EntireTableInfo entireTableInfo)
throws DatabaseModelResolverException
{
boolean notEditable = false;
try
{
DatabaseMetaData metaData = cn.getMetaData();
notEditable = metaData.isReadOnly();
}
catch (SQLException e)
{
}
if (notEditable)
modelBuilder.addFeature(NotEditable.class);
}
/**
* 判断给定{@linkplain ColumnInfo}是否是主键
*

View File

@ -4,6 +4,7 @@
package org.datagear.model.features;
import org.datagear.model.ModelFeature;
import org.datagear.model.PropertyFeature;
/**
@ -12,7 +13,7 @@ import org.datagear.model.PropertyFeature;
* @author datagear@163.com
*
*/
public interface NotEditable extends PropertyFeature
public interface NotEditable extends ModelFeature, PropertyFeature
{
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* JDBC驱动兼容标注
* <p>
* 为了兼容某些JDBC驱动程序部分代码会有特殊逻辑可使用此类标注说明
* </p>
*
* @author datagear@163.com
*
*/
@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE })
@Retention(value = RetentionPolicy.SOURCE)
public @interface JDBCCompatiblity
{
/**
* 原因
*
* @return
*/
String value() default "";
}

View File

@ -735,6 +735,17 @@ public class JdbcUtil
return true;
}
/**
* 是否只读
*
* @param cn
* @return
*/
public static boolean isReadonly(Connection cn) throws SQLException
{
return cn.isReadOnly();
}
/**
* 设置为只读
*
@ -742,6 +753,7 @@ public class JdbcUtil
* @param readonly
* @return {@code true} 设置成功{@code false} 不支持
*/
@JDBCCompatiblity("某些驱动程序可能不支持此方法而抛出异常比如Hive jdbc")
public static boolean setReadonlyIfSupports(Connection cn, boolean readonly)
{
try
@ -750,7 +762,6 @@ public class JdbcUtil
return true;
}
// 某些驱动程序可能不支持此方法而抛出异常比如Hive jdbc
catch (SQLException e)
{
return false;
@ -764,6 +775,7 @@ public class JdbcUtil
* @param autoCommit
* @return {@code true} 设置成功{@code false} 不支持
*/
@JDBCCompatiblity("避免有驱动程序不支持此方法而抛出异常")
public static boolean setAutoCommitIfSupports(Connection cn, boolean autoCommit)
{
try
@ -785,6 +797,7 @@ public class JdbcUtil
* @param cn
* @return 连接URL{@code null} 不支持时
*/
@JDBCCompatiblity("避免有驱动程序不支持此方法而抛出异常")
public static String getURLIfSupports(Connection cn)
{
try
@ -792,7 +805,6 @@ public class JdbcUtil
DatabaseMetaData metaData = cn.getMetaData();
return metaData.getURL();
}
// 避免有驱动程序不支持此方法而抛出异常
catch (Throwable t)
{
return null;
@ -805,6 +817,7 @@ public class JdbcUtil
* @param cn
* @return 连接用户名{@code null} 不支持时
*/
@JDBCCompatiblity("避免有驱动程序不支持此方法而抛出异常")
public static String getUserNameIfSupports(Connection cn)
{
try
@ -812,7 +825,6 @@ public class JdbcUtil
DatabaseMetaData metaData = cn.getMetaData();
return metaData.getUserName();
}
// 避免有驱动程序不支持此方法而抛出异常
catch (Throwable t)
{
return null;
@ -826,6 +838,7 @@ public class JdbcUtil
* @param row
* @throws SQLException
*/
@JDBCCompatiblity("避免有驱动程序不支持absolute而抛出异常")
public static void moveToBeforeRow(ResultSet rs, int row) throws SQLException
{
// 第一行不做任何操作避免不必要的调用可能导致底层不支持而报错
@ -838,7 +851,6 @@ public class JdbcUtil
}
catch (SQLException e)
{
// 某些驱动程序不支持absolute那么转为next方式
for (int i = 1; i < row; i++)
{
if (!rs.next())
@ -854,6 +866,7 @@ public class JdbcUtil
* @return
* @throws SQLException
*/
@JDBCCompatiblity("避免有驱动程序不支持TYPE_SCROLL_INSENSITIVE而抛出异常")
public static Statement createQueryStatement(Connection cn) throws SQLException
{
try
@ -862,7 +875,6 @@ public class JdbcUtil
}
catch (SQLFeatureNotSupportedException e)
{
// 驱动程序不支持TYPE_SCROLL_INSENSITIVE
return cn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
}
@ -875,6 +887,7 @@ public class JdbcUtil
* @return
* @throws SQLException
*/
@JDBCCompatiblity("避免有驱动程序不支持TYPE_SCROLL_INSENSITIVE而抛出异常")
public static PreparedStatement createQueryPreparedStatement(Connection cn, String sql) throws SQLException
{
try
@ -931,9 +944,10 @@ public class JdbcUtil
}
}
// 可能是查询SQL语句不支持ResultSet.TYPE_SCROLL_*比如SQLServer的聚集列存储索引
// 因此这里再降级为ResultSet.TYPE_FORWARD_ONLY执行查询
@JDBCCompatiblity("可能是查询SQL语句不支持ResultSet.TYPE_SCROLL_*比如SQLServer的聚集列存储索引"
+ "因此这里再降级为ResultSet.TYPE_FORWARD_ONLY再执行查询")
// TODO 如果是SQL语法错误则不应再降级执行
QueryResultSet queryResultSet = null;
st = cn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
@ -942,7 +956,9 @@ public class JdbcUtil
rs = st.executeQuery(sql);
return new QueryResultSet(st, rs);
queryResultSet = new QueryResultSet(st, rs);
return queryResultSet;
}
/**
@ -997,9 +1013,10 @@ public class JdbcUtil
}
}
// 可能是查询SQL语句不支持ResultSet.TYPE_SCROLL_*比如SQLServer的聚集列存储索引
// 因此这里再降级为ResultSet.TYPE_FORWARD_ONLY执行查询
@JDBCCompatiblity("可能是查询SQL语句不支持ResultSet.TYPE_SCROLL_*比如SQLServer的聚集列存储索引"
+ "因此这里再降级为ResultSet.TYPE_FORWARD_ONLY再执行查询")
// TODO 如果是SQL语法错误则不应再降级执行
QueryResultSet queryResultSet = null;
pst = cn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
@ -1008,7 +1025,9 @@ public class JdbcUtil
rs = pst.executeQuery();
return new QueryResultSet(pst, rs);
queryResultSet = new QueryResultSet(pst, rs);
return queryResultSet;
}
/**

View File

@ -29,6 +29,7 @@ import org.datagear.management.domain.User;
import org.datagear.management.service.SchemaService;
import org.datagear.model.Model;
import org.datagear.model.Property;
import org.datagear.model.features.NotEditable;
import org.datagear.model.support.MU;
import org.datagear.model.support.PropertyPath;
import org.datagear.model.support.PropertyPathInfo;
@ -275,11 +276,11 @@ public class DataController extends AbstractSchemaModelConnController
ModelUtils.displayName(model, WebUtils.getLocale(request)));
springModel.addAttribute(KEY_TITLE_DISPLAY_DESC,
ModelUtils.displayDesc(model, WebUtils.getLocale(request)));
setGridPageAttributes(request, response, springModel, schema, model);
}
}.execute();
setGridPageAttributes(request, springModel);
return "/data/data_grid";
}
@ -767,11 +768,11 @@ public class DataController extends AbstractSchemaModelConnController
springModel.addAttribute(KEY_TITLE_DISPLAY_NAME,
ModelUtils.displayName(model, propertyPathObj, WebUtils.getLocale(request)));
setGridPageAttributes(request, response, springModel, schema, model);
}
}.execute();
setGridPageAttributes(request, springModel);
return "/data/data_select_prop_value";
}
@ -1172,11 +1173,11 @@ public class DataController extends AbstractSchemaModelConnController
springModel.addAttribute(KEY_TITLE_DISPLAY_NAME,
ModelUtils.displayName(model, propertyPathObj, WebUtils.getLocale(request)));
springModel.addAttribute("isPrivateProperty", ModelUtils.isPrivatePropertyTail(propertyPathInfo));
setGridPageAttributes(request, response, springModel, schema, model);
}
}.execute();
setGridPageAttributes(request, springModel);
return "/data/data_prop_value_grid";
}
@ -1262,11 +1263,11 @@ public class DataController extends AbstractSchemaModelConnController
springModel.addAttribute(KEY_TITLE_DISPLAY_NAME,
ModelUtils.displayName(model, propertyPathObj, WebUtils.getLocale(request)));
springModel.addAttribute("isPrivateProperty", ModelUtils.isPrivatePropertyTail(propertyPathInfo));
setGridPageAttributes(request, response, springModel, schema, model);
}
}.execute();
setGridPageAttributes(request, springModel);
return "/data/data_prop_value_grid";
}
@ -2111,10 +2112,14 @@ public class DataController extends AbstractSchemaModelConnController
* @param request
* @param springModel
*/
protected void setGridPageAttributes(HttpServletRequest request, org.springframework.ui.Model springModel)
protected void setGridPageAttributes(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, Schema schema, Model model)
{
springModel.addAttribute("queryLeftClobLengthOnReading", this.queryLeftClobLengthOnReading);
if (model.hasFeature(NotEditable.class))
springModel.addAttribute("readonly", true);
// 编辑表格需要表单属性
setFormPageAttributes(request, springModel);
}

View File

@ -7,9 +7,11 @@ Object data 初始数据允许null
String propertyPath 属性名称不允许null
String titleDisplayName 页面展示名称,默认为""
List PropertyPathDisplayName conditionSource 可用的查询条件列表不允许为null
boolean readonly 是否只读操作默认为false
boolean isMultipleSelect 是否多选默认为false
-->
<#assign titleDisplayName=(titleDisplayName!'')>
<#assign readonly=(readonly!false)>
<#assign isMultipleSelect=(isMultipleSelect!false)>
<html>
<head>
@ -31,8 +33,11 @@ boolean isMultipleSelect 是否多选默认为false
</div>
<div class="operation">
<input name="confirmButton" type="button" class="recommended" value="<@spring.message code='confirm' />" />
<#if !readonly>
<input name="addButton" type="button" value="<@spring.message code='add' />" />
<input name="editButton" type="button" value="<@spring.message code='edit' />" />
</#if>
<input name="viewButton" type="button" value="<@spring.message code='view' />" />
</div>
</div>
@ -127,6 +132,7 @@ boolean isMultipleSelect 是否多选默认为false
}
});
<#if !readonly>
po.element("input[name=addButton]").click(function()
{
var options =
@ -163,6 +169,7 @@ boolean isMultipleSelect 是否多选默认为false
});
});
});
</#if>
po.element("input[name=viewButton]").click(function()
{