在CRUD操作中与业务无关的SQL字段赋值的方法_数据库其它

提高效率一直是个永恒的话题,编程中有一项也是可以提到效率的,那就是专注做一件事情,让其它没有强紧密联系的与之分开。这里分享下我们做CRUD时遇到的常见数据处理场景:

•数据库表字段全部设计为非空,即使这个字段在业务上是可以为空的,之所以将数据库表字段全部设计为非空,这里有优点也有缺点,我们认为优点大于缺点,所以选择了它

优点:

1.获取值时,不用判断这个字段是否为null,直接可用于逻辑运算。

2.mysql DBA推荐此方案,可能是有利于性能,这里我并非求证过。

缺点:

1.业务含义没有null清楚,比如int字段默认值设置成0,0就没有null语义清晰。

2.在使用ORM插入数据时,需要处理非空字段值为null的问题。

• 系统字段的赋值,比如创建人,创建人id,创建时间,编辑人,编辑人id,编辑时间等,这些都需要在实际插入数据库前赋值给Model。这些系统字段与具体的业务一般没有太大的关联关系,只是起到标注数据被什么人在什么时间处理的,当这些非业务相关的代码充斥在代码中时,就显得有些多余,而且这类代码多了也会显示冗余,最后带来的结果就是非关键代码比例大。

上面关于默认值与null语义问题不需要解决,因为我们认为具有默认值带来的优点远大于可空字段带来的烦恼,我们来看默认值与系统字段一般情况下如何处理:

•在操作ORM时,将模型所有可空的字段都手动赋值成默认值,int的赋值为0等。

•在设计数据库时,将非空字段加上默认值,让数据库来处理这些未插入值的字段,如果使用mybatis的话,mapper中提到的插入操作有两个:insert,insertSelective,后面这个insertSelective就是处理非空字段的,即插入的模型对于不需要赋值的字段就保持null值,数据库在插入时生成的sql语句也不会包含这些字段,这样就可以利用上数据库的默认值了。如果正巧数据库的结构当初设计时没有设计默认值,又不能改的情况就比较糟糕了,情况回到上面手动赋值,可能会出现类似如下的代码:编写一个函数通过反射来解析每个字段,如果为null就修改为默认值:

public static <T> void emptyNullValue(final T model) {
Class<?> tClass = model.getClass();
List<Field> fields = Arrays.asList(tClass.getDeclaredFields());
for (Field field : fields) {
Type t = field.getType();
field.setAccessible(true);
try {
if (t == String.class && field.get(model) == null) {
field.set(model, "");
} else if (t == BigDecimal.class && field.get(model) == null) {
field.set(model, new BigDecimal(0));
} else if (t == Long.class && field.get(model) == null) {
field.set(model, new Long(0));
} else if (t == Integer.class && field.get(model) == null) {
field.set(model, new Integer(0));
} else if (t == Date.class && field.get(model) == null) {
field.set(model, TimeHelper.LocalDateTimeToDate(java.time.LocalDateTime.of(1990, 1, 1, 0, 0, 0, 0)));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
} 

然后在代码调用insert前调用函数来解决:

ModelHelper.emptyNullValue(request); 

如何处理系统字段呢,在创建编辑数据时,需要获取当前用户,然后根据逻辑分别更新创建人信息以及编辑人信息,我们专门编写一个反射机制的函数来处理系统字段:

注:下面的系统字段的识别,是靠系统约定实现的,比如creator约定为创建人等,可根据不同的情况做数据兼容,如果系统设计的好,一般在一个系统下所有表的风格应该是相同的。

public static <T> void buildCreateAndModify(T model,ModifyModel modifyModel,boolean isCreate){
Class<?> tClass = model.getClass();
List<Field> fields = Arrays.asList(tClass.getDeclaredFields());
for (Field field : fields) {
Type t = field.getType();
field.setAccessible(true);
try {
if(isCreate){
if (field.getName().equals(modifyModel.getcId())) {
field.set(model, modifyModel.getUserId());
}
if (field.getName().equals(modifyModel.getcName())) {
field.set(model, modifyModel.getUserName());
}
if (field.getName().equals(modifyModel.getcTime())) {
field.set(model, new Date());
}
}
if (field.getName().equals(modifyModel.getmId())) {
field.set(model, modifyModel.getUserId());
}
if (field.getName().equals(modifyModel.getmName())) {
field.set(model, modifyModel.getUserName());
}
if (field.getName().equals(modifyModel.getmTime())) {
field.set(model, new Date());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

最后在数据处理前,根据创建或者编辑去调用函数来给系统字段赋值,这类代码都混杂在业务代码中。

ModifyModel modifyModel = new ModifyModel();
modifyModel.setUserId(getCurrentEmployee().getId());
modifyModel.setUserName(getCurrentEmployee().getName());
if (request.getId() == 0) {
ModelHelper.buildCreateAndModify(request, modifyModel, true);
deptService.insert(request);
} else {
ModelHelper.buildCreateAndModify(request, modifyModel, false);
deptService.updateByPrimaryKey(request);
}

我们可以利用参数注入来解决。参数注入的理念就是在spring mvc接收到前台请求的参数后,进一步对接收到的参数做处理以达到预期的效果。我们来创建

ManageModelConfigMethodArgumentResolver,它需要实现HandlerMethodArgumentResolver,这个接口看起来比较简单,包含两个核心方法:

• 判断是否是需要注入的参数,一般通过判断参数上是否有特殊的注解来实现,也可以增加一个其它的参数判断,可根据具体的业务做调整,我这里只以是否有特殊注释来判定是否需要参数注入。

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(ManageModelConfig.class);
}

• 参数注入,它提供了一个扩展入口,让我们有机会对接收到的参数做进一步的处理。

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object manageModel =getRequestResponseBodyMethodProcessor().resolveArgument(parameter, mavContainer, webRequest, binderFactory);
ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
Employee currentUser = (Employee) servletRequest.getAttribute(DEFAULT_ATTRIBUTE_GET_USER_FROM_REQUEST);
if (null == currentUser)
{
return manageModel;
}
ManageModelConfig parameterAnnotation = parameter.getParameterAnnotation(ManageModelConfig.class);
ModelHelper.setDefaultAndSystemFieldsValue(manageModel, currentUser,parameterAnnotation.isSetDefaultFieldsValue());
return manageModel;
}

这段函数有几处核心逻辑:

•取得参数对象,因为我们处理的是ajax请求的参数,最简单的注入方法就是得到实际参数通过反射去处理默认字段以及系统的值。ajax请求与form表单post提交的数据绑定略有不同,可参考之前文章分享的列表页动态搜索的参数注入(列表页的动态条件搜索)。获取当前请求参数对象,我们可以借助如下两个对象配合来完成:

•RequestMappingHandlerAdapter

•RequestResponseBodyMethodProcessor

private RequestMappingHandlerAdapter requestMappingHandlerAdapter=null;
private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
private RequestResponseBodyMethodProcessor getRequestResponseBodyMethodProcessor() {
if(null==requestMappingHandlerAdapter)
{
requestMappingHandlerAdapter=new RequestMappingHandlerAdapter();
}
if (null==requestResponseBodyMethodProcessor) {
List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
messageConverters.add(new MappingJackson2HttpMessageConverter());
requestResponseBodyMethodProcessor = new RequestResponseBodyMethodProcessor(messageConverters);
}
return requestResponseBodyMethodProcessor;
} 

通过如下代码就可以取到参数对象了,其实就是让spring mvc重新解析了一遍参数。

Object manageModel =getRequestResponseBodyMethodProcessor().resolveArgument(parameter, mavContainer, webRequest, binderFactory); 

•如何获取当前用户,我们在成功登录系统后,将当前用户的信息存储在request中,然后就可以在函数中获取当前用户,也可以采用其它方案,比如ThreadLocal,缓存等等。

ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
Employee currentUser = (Employee) servletRequest.getAttribute(DEFAULT_ATTRIBUTE_GET_USER_FROM_REQUEST); 

•调用处理函数解决默认字段以及系统的赋值,可以根据配置来决定是否处理字段默认值。

ManageModelConfig parameterAnnotation = parameter.getParameterAnnotation(ManageModelConfig.class);
ModelHelper.setDefaultAndSystemFieldsValue(manageModel, currentUser,parameterAnnotation.isSetDefaultFieldsValue());

最后将我们的参数注入逻辑启动起来,这里选择在xml中配置:

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:argument-resolvers>
<bean class="cn.wanmei.party.management.common.mvc.method.annotation.ManageModelConfigMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven> 

再看action中的调用:只需要在参数前面增加注解@ManageModelConfig,如果需要处理默认值,则将启用默认值的选项设置成true即可,下面的实现部分完全看不到任何与业务无关的代码。

@RequestMapping(value = "/addOrUpdateUser")
@ResponseBody
public Map<String, Object> addOrUpdateUser(@ManageModelConfig(isSetDefaultFieldsValue=true) EmployeeDto request) {
Map<String, Object> ret = new HashMap<>();
ValidateUtil.ValidateResult result= new ValidateUtil().ValidateModel(request);
boolean isCreate=request.getId() == 0;
try {
if (isCreate)
{
employeeService.insert(request);
}
else
{
employeeService.updateByPrimaryKey(request);
}
ret.put("data", "ok");
}catch (Exception e){
ret.put("err", e.getMessage());
}
return ret;
}

通过自定义实现HandlerMethodArgumentResolver,来捕获ajax请求的参数,利用反射机制动态的将系统字段以及需要处理默认值的字段自动赋值,避免人工干预,起到了代码精简,逻辑干净,问题统一处理的目的。需要注意的是这些实现都是结合当前系统设计的,比如我们认为id字段>0就代表是更新操作,为空或者等于小于0就代表是创建,系统字段也是约定名称的等等。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索sql字段赋值
sql数据库给字段赋值、数据库增加字段并赋值、数据库crud、数据库crud操作、数据库的crud,以便于您获取更多的相关知识。

时间: 2024-10-31 05:15:29

在CRUD操作中与业务无关的SQL字段赋值的方法_数据库其它的相关文章

ASP.NET防范SQL注入式攻击的方法_实用技巧

一.什么是SQL注入式攻击?  SQL注入式攻击就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令.在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击.常见的SQL注入式攻击过程类如:  ⑴ 某个ASP.NET Web应用有一个登录页面,这个登录页面控制着用户是否有权访问应用,它要求用户输入一个名称和密码.  ⑵ 登录页面中输入的内容将直接用来构造动态的SQL命令,或

Access数据库升级为SQL数据库的注意事项和方法_数据库其它

很多Access和SQL Server开发人员都经常面临着将一个Access数据库升级到SQL Server数据库的问题.由于存在现有的Access升级向导,这一转变的过程就会变得相当简单,尤其是当你建立一个与SQL Server数据相联系的ADP的时候.然而,向导并不是十全十美的,需要解决的问题还是大有存在. 首先,有些对象并不是简单的升级,所以这时你不得不人为地处理.第二,很多Access特性──比如一些查询类型,对象,以及特定的数据类型在你没有做好升级之前的准备的情况下就会导致错误的产生.

用SQL Server访问Sybase中的表的方法_数据库其它

问:SQL Server应该怎样访问Sybase数据库的表?  答:具体方法如下:  1: 安装Sybase客户端  版本的要求:  Sybase Client 11.9.2 for NT  1.1:安装完成后,运行开始->程序->Sybase->Dsedit  1.2:选择菜单的'Server Object',-> 'Add' 输入服务名 比如 1.70  1.3:然后在Server列表中选择'1.70',双击右边的对话框server address栏,在Network Addr

SQL 注入式攻击的本质_数据库其它

更没想到的是这么老掉牙的东西居然还能跑出来搅风搅雨,而且造成了如此大的破坏,有文章还说注入式攻击还会有"第三波"攻击潮,到时候会更加难以察觉,连微软的大佬都跑出来澄清说与微软的技术与编码无关,微软为此还专门推出了三个检测软件,那么这个SQL注入式攻击的漏洞究竟是怎么造成的呢? 正如微软的大佬所说的,是由于网站程序的开发人员编码不当造成的,不光是ASP.ASP.NET,也包括JSP.PHP等技术,受影响的也不仅仅是Access和SQL Server数据库,也包括Oracle和MySql等

推荐Sql server一些常见性能问题的解决方法_数据库其它

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0 3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放

在WINXP系统上安装SQL Server企业版的方法_应用技巧

先讲一下注意事项:必须启用"文件和打印共享"才能成功安装MSDE.否则安装进程可能会继续进行直到几乎完成, 然后又意外回滚,无法完成MSDE的安装. 我上次安装失败就是这个原因,平时我总是关闭SERVER服务,也就没有什么共享.顺便提一句,据说你试着把计算机名改成大写字母,那在安装SQL的时候就不会失败了,但是我没验证! XP系统下也可以安装 企业版本 安装办法如下: 一.在SQL服务器的安装盘中找到MSDE这个目录,并且点击setup.exe安装它,过程简单直接下一步就OK了.(这个

Access转成SQL数据库的方法_数据库其它

首先,我说的是在ACCESS2000,SQL2000之间转换,其他的我也还没有尝试过,希望大家多多试验,肯定是有办法的. 转换的方法 1.打开"控制面板"下"管理工具"中的"数据库源". 2.按"添加"添加一个新的数据源,在选择栏里选"Driver do microsoft Access (*.mdb)",完成后将出现一个框,在"数据库源"里面输入你想写的名称,我取名叫"ABC

识别 MS SQL 各个版本的版本号的方法_数据库其它

概要本文介绍如何识别当前的 SQL Server 版本号以及对应的产品级别或服务包 (SP) 级别.还介绍了如何识别所使用的 SQL Server 2000 或 SQL Server 7.0 的具体版本.  如何确定所运行的 SQL Server 2005 的版本 要确定所运行的 SQL Server 2005 的版本,请使用 SQL Server Management Studio 连接到 SQL Server 2005,然后运行以下 Transact-SQL 语句:  SELECT     

SQL语句详细说明[部分]_数据库其它

SQL 语句教程 (11) Group By 我们现在回到函数上.记得我们用SUM这个指令来算出所有的 Sales (营业额)吧!如果我们的需求变成是要算出每一间店 (store_name) 的营业额 (sales),那怎么办呢?在这个情况下,我们要做到两件事:第一,我们对于 store_name 及 Sales 这两个栏位都要选出.第二,我们需要确认所有的 sales 都要依照各个 store_name 来分开算.这个语法为:SELECT "栏位1", SUM("栏位2&q