forked from p81075629/datagear
Model添加NotEditable特性,用于支持只读数据库,并修改相关逻辑
This commit is contained in:
parent
1bce008153
commit
91be070466
|
@ -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}是否是主键。
|
||||
*
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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 "";
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue