今天在开发中发现一个问题,本来想对一个VO对象的removed值赋值,然后去update一下这条记录,一个最简单的set方法,但是在调用时直接抛异常了。

  1: public void setRemoved(Date removed) {
  2:     this.removed = removed;
  3: }

当时很诧异,没有想到这地方会出问题,后来看代码才发现原来cs在这里有拦截器,com.cloud.utils.db.UpdateBuilder#intercept

  1: @Override
  2: public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  3:     String name = method.getName();
  4:     if (name.startsWith("set")) {
  5:         String field = methodToField(name, 3);
  6:         makeChange(field, args[0]);
  7:     } else if (name.startsWith("incr")) {
  8:         makeIncrChange(name, args);
  9:     } else if (name.startsWith("decr")) {
 10:         makeDecrChange(name, args);
 11:     }
 12:     return methodProxy.invokeSuper(object, args);
 13: }

所有set开头的方法都被拦截,转调com.cloud.utils.db.UpdateBuilder#makeChange

  1: protected Attribute makeChange(String field, Object value) {
  2:     Attribute attr = _dao._allAttributes.get(field);
  3:
  4:     assert (attr == null || attr.isUpdatable()) : "Updating an attribute that's not updatable: " + field;
  5:     if (attr != null) {
  6:         if (attr.attache == null) {
  7:             _changes.put(field, new Ternary<Attribute, Boolean, Object>(attr, null, value));
  8:         } else {
  9:             if (_collectionChanges == null) {
 10:                 _collectionChanges = new HashMap<Attribute, Object>();
 11:             }
 12:             _collectionChanges.put(attr, value);
 13:         }
 14:     }
 15:     return attr;
 16: }

在那句assert那里,会判断attr是否可以update。

Attribute是cloudstack的基类GenericDaoBase中保存的对象属性集合,是一个保存了数据表的列名以及列属性的Map对象,这个对象通过Java JPA来进行持久化。

每一个干活的DAO对象都会继承于GenericDaoBase,并且实现自己这类对象的接口,如下图:

刚才说的那个isUpdatable()是个人认为cloudstack中有关数据库权限做的很精巧的地方

  1: public final boolean isUpdatable() {
  2:     return Flag.Updatable.check(flags);
  3: }

这里面会调用Flag这个enum对象的check方法,那个flags同样也是在调用Attribute的构造方法时生成的,它根据数据库表的每一列的属性,按照一个逻辑去计算相关的权限,flags的初始值为0,然后针对每一种权限依次去做或运算,最后保存为一个值,遇到需要校验权限的地方,就用下面的check方法,看传进来的权限,比如Flag.Updatable,与flags去与运算,如果与完了的结果与传进来的权限值一样,那么可以继续进行,否则就会由于assert计算表达式的结果为false而退出去。

  1: public boolean check(int value) {
  2:     return (value & place) == place;
  3: }

今天的问题就在这,Attribute.flags的值导致assert的表达式结果为false,在初始化时,flags的值明明为135(128+4+2+1),但是在实际计算时却不是这个值,一度很不解。但是为了解决生产环境的问题,不能在这个细节上耽搁太久,于是换了个思路,尝试着调用DAO的remove方法,也能实现针对数据库表的removed字段赋值。具体到为什么flags的值变了,还需要抽时间好好研究一下。

不过值得欣慰的是,如果不是set时抛异常了,自己可能也不会去啃这部分代码来了解。

附1,计算flags时的代码(com.cloud.utils.db.Attribute#setupColumnInfo):

  1: protected void setupColumnInfo(Class<?> clazz, AttributeOverride[] overrides, String tableName, boolean isEmbedded, boolean isId) {
  2:     flags = Flag.Selectable.setTrue(flags);
  3:     GeneratedValue gv = field.getAnnotation(GeneratedValue.class);
  4:     if (gv != null) {
  5:         if (gv.strategy() == GenerationType.IDENTITY) {
  6:             flags = Flag.DbGenerated.setTrue(flags);
  7:         } else if (gv.strategy() == GenerationType.SEQUENCE) {
  8:             assert (false) : "Sequence generation not supported.";
  9:             flags = Flag.DaoGenerated.setTrue(flags);
 10:             flags = Flag.Insertable.setTrue(flags);
 11:             flags = Flag.SequenceGV.setTrue(flags);
 12:         } else if (gv.strategy() == GenerationType.TABLE) {
 13:             flags = Flag.DaoGenerated.setTrue(flags);
 14:             flags = Flag.Insertable.setTrue(flags);
 15:             flags = Flag.TableGV.setTrue(flags);
 16:         } else if (gv.strategy() == GenerationType.AUTO) {
 17:             flags = Flag.DaoGenerated.setTrue(flags);
 18:             flags = Flag.Insertable.setTrue(flags);
 19:             flags = Flag.AutoGV.setTrue(flags);
 20:         }
 21:     }
 22:
 23:     if (isEmbedded) {
 24:         flags = Flag.Embedded.setTrue(flags);
 25:     }
 26:
 27:     if (isId) {
 28:         flags = Flag.Id.setTrue(flags);
 29:     } else {
 30:         Id id = field.getAnnotation(Id.class);
 31:         if (id != null) {
 32:             flags = Flag.Id.setTrue(flags);
 33:         }
 34:     }
 35:     column = field.getAnnotation(Column.class);
 36:     if (gv == null) {
 37:         if (column == null || (column.insertable() && column.table().length() == 0)) {
 38:             flags = Flag.Insertable.setTrue(flags);
 39:         }
 40:         if (column == null || (column.updatable() && column.table().length() == 0)) {
 41:             flags = Flag.Updatable.setTrue(flags);
 42:         }
 43:         if (column == null || column.nullable()) {
 44:             flags = Flag.Nullable.setTrue(flags);
 45:         }
 46:         Encrypt encrypt = field.getAnnotation(Encrypt.class);
 47:         if (encrypt != null && encrypt.encrypt()) {
 48:             flags = Flag.Encrypted.setTrue(flags);
 49:         }
 50:     }
 51:     ElementCollection ec = field.getAnnotation(ElementCollection.class);
 52:     if (ec != null) {
 53:         flags = Flag.Insertable.setFalse(flags);
 54:         flags = Flag.Selectable.setFalse(flags);
 55:     }
 56:
 57:     Temporal temporal = field.getAnnotation(Temporal.class);
 58:     if (temporal != null) {
 59:         if (temporal.value() == TemporalType.DATE) {
 60:             flags = Flag.Date.setTrue(flags);
 61:         } else if (temporal.value() == TemporalType.TIME) {
 62:             flags = Flag.Time.setTrue(flags);
 63:         } else if (temporal.value() == TemporalType.TIMESTAMP) {
 64:             flags = Flag.TimeStamp.setTrue(flags);
 65:         }
 66:     }
 67:
 68:     if (column != null && column.table().length() > 0) {
 69:         table = column.table();
 70:     }
 71:
 72:     columnName = DbUtil.getColumnName(field, overrides);
 73: }

附2,数据库表每一列可能的所有权限列表:

CloudStack的VO在调用setRemoved方法抛异常的原因的更多相关文章

  1. ABP在领域事件中异步调用方法抛异常

    在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...

  2. 执行ArrayList的remove(object)方法抛异常?

    简介 或许有很多小伙伴都尝试过如下的代码: ArrayList<Object> list = ...; for (Object object : list) { if (条件成立) { l ...

  3. Spring AOP不拦截从对象内部调用的方法原因

    拦截器的实现原理很简单,就是动态代理,实现AOP机制.当外部调用被拦截bean的拦截方法时,可以选择在拦截之前或者之后等条件执行拦截方法之外的逻辑,比如特殊权限验证,参数修正等操作. 但是最近在项目中 ...

  4. 抛异常 throw的注意事项

    子类覆盖父类只能抛出父类的异常或者子类或者子集注意:如果父类的方法没有抛异常,那么子类覆盖时绝对不能抛. 子类继承父类时,方法抛异常,要么抛父类,要么抛父类下的子类,不能抛父类平级或以上的异常 原因是 ...

  5. .Net中的AOP系列之《间接调用——拦截方法》

    返回<.Net中的AOP>系列学习总目录 本篇目录 方法拦截 PostSharp方法拦截 Castle DynamicProxy方法拦截 现实案例--数据事务 现实案例--线程 .Net线 ...

  6. C++调用JAVA方法详解

    C++调用JAVA方法详解          博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...

  7. 原!! java直接打印一个对象时,并不是直接调用该类的toString方法 ,而是会先判断是否为null,非null才会调用toString方法

    网上看了好多java直接打印一个对象时,直接调用该类的toString方法 . 但是: Object obj=null; System.out.println(obj);//没有报错 System.o ...

  8. JNI调用native方法出现 java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()异常的解决办法

    昨天拿到JNI的Android工程Demo,然后把demo整合到开发的主线工程上,发现调用JNI方法一直抛同一个异常 java.lang.UnsatisfiedLinkError: XXXclass. ...

  9. Java 反射 Method的invoke回调调用任意方法

    Java 反射 Method的invoke回调调用任意方法 @author ixenos 关键子:Method.Field.invoke方法指针/函数指针.回调函数 invoke回调流程示例 0.由C ...

随机推荐

  1. 中文+django1.9+python3.5一些注意点

    1.模板html文件里一定要加 <!DOCTYPE html><meta http-equiv="Content-type" content="text ...

  2. Linux 下部署单机 hadoop 测试

    最终运行结果展示: 格式化namenode. 开始测试 显示测试进程 浏览器查看效果展示:(虽然还不清楚是什么意思,但是能看到这个效果已经很开心了) 话不多说,进入主题: 1. 安装 VMwareSt ...

  3. PHP 新建动态类的代码

    $testObject=(object)array(); $testObject->first="firstValue"; var_dump($testObject); $t ...

  4. SQL SERVER 2008 R2 SP3 发布

    今晚上刚发现,微软很低调啊 下载地址:http://www.microsoft.com/zh-cn/download/details.aspx?id=44271 整合SP3的Express系列版本还没 ...

  5. 20+非常棒的Photoshop卡通设计教程

    现在把一个人的脸变成卡通图案再用它来当头像这种现象使非常常见的,同样的卡通插图可以用于多种渠道的设计.网上有很多公司都会创立一种吉祥物并把它应用到市场营销中.因为有了类似于photoshop这样强大的 ...

  6. Linux下U盘的挂载和文件的拷贝

    把文件通过U盘拷贝到linux系统下插好U盘后,查看磁盘情况fdisk -l正常情况下有 Disk /dev/sda:2045 MB,2045247488 bytes47 heads,46 secto ...

  7. navicat 或者workbench 无法连接127.0.0.1(61)的解决方法

    1.输入mysql -uroot 进入命令行模式, 2.输入"show variables like '%sock%';"查看sock文件所在位置 如: 3.配置客户端(以navi ...

  8. hadoop2.6.0 --- 64位源代码

    今天有朋友在群里找hadoop最新的2.6.0的源代码,其实这个源代码在hadoop的官方网站是有下载的(应该是32位的),还有一个src,不过给的是maven版本,需要自己在机器上编译一下(我的机器 ...

  9. android学习笔记---发送状态栏通知

    发送消息的代码如下: //获取通知管理器 NotificationManager mNotificationManager = (NotificationManager) getSystemServi ...

  10. cocos中添加显示文字的三种方式(CCLabelTTF 、CCLabelBMFont 和CCLabelAtlas)

    CCLabelTTF CCLabelTTF 每次调用 setString (即改变文字)的时候,一个新的OPENGL 纹理将会被创建..这意味着setString 和创建一个新的标签一样慢. 这个类使 ...