CloudStack的VO在调用setRemoved方法抛异常的原因
今天在开发中发现一个问题,本来想对一个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方法抛异常的原因的更多相关文章
- ABP在领域事件中异步调用方法抛异常
在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...
- 执行ArrayList的remove(object)方法抛异常?
简介 或许有很多小伙伴都尝试过如下的代码: ArrayList<Object> list = ...; for (Object object : list) { if (条件成立) { l ...
- Spring AOP不拦截从对象内部调用的方法原因
拦截器的实现原理很简单,就是动态代理,实现AOP机制.当外部调用被拦截bean的拦截方法时,可以选择在拦截之前或者之后等条件执行拦截方法之外的逻辑,比如特殊权限验证,参数修正等操作. 但是最近在项目中 ...
- 抛异常 throw的注意事项
子类覆盖父类只能抛出父类的异常或者子类或者子集注意:如果父类的方法没有抛异常,那么子类覆盖时绝对不能抛. 子类继承父类时,方法抛异常,要么抛父类,要么抛父类下的子类,不能抛父类平级或以上的异常 原因是 ...
- .Net中的AOP系列之《间接调用——拦截方法》
返回<.Net中的AOP>系列学习总目录 本篇目录 方法拦截 PostSharp方法拦截 Castle DynamicProxy方法拦截 现实案例--数据事务 现实案例--线程 .Net线 ...
- C++调用JAVA方法详解
C++调用JAVA方法详解 博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...
- 原!! java直接打印一个对象时,并不是直接调用该类的toString方法 ,而是会先判断是否为null,非null才会调用toString方法
网上看了好多java直接打印一个对象时,直接调用该类的toString方法 . 但是: Object obj=null; System.out.println(obj);//没有报错 System.o ...
- JNI调用native方法出现 java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()异常的解决办法
昨天拿到JNI的Android工程Demo,然后把demo整合到开发的主线工程上,发现调用JNI方法一直抛同一个异常 java.lang.UnsatisfiedLinkError: XXXclass. ...
- Java 反射 Method的invoke回调调用任意方法
Java 反射 Method的invoke回调调用任意方法 @author ixenos 关键子:Method.Field.invoke方法指针/函数指针.回调函数 invoke回调流程示例 0.由C ...
随机推荐
- Linux基本命令(7)文件阅读的命令
文件阅读的命令 命令 功能 head 查看文件的开头部分 tail 查看文件结尾的10行 less less是一个分页工具,它允许一页一页地(或一个屏幕一个屏幕地)查看信息 more more是一个分 ...
- Selenium2Library系列 keywords 之 _SelectElementKeywords 之 select_all_from_list(self, locator)
def select_all_from_list(self, locator): """Selects all values from multi-select list ...
- php codeigniter (CI) oracle 数据库配置-宋正河整理
database.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $active_group = 'default'; $active_record ...
- C++中 类的构造函数理解(一)
C++中 类的构造函数理解(一) 写在前面 这段时间完成三个方面的事情: 1.继续巩固基础知识(主要是C++ 方面的知识) 2.尝试实现一个iOS的app,通过完成app,学习iOS开发中要用到的知识 ...
- Oracle 学习用
最近在学习Oracle数据库 ,公司用这个,学习到现在,唉!苦逼的程序员呀! 也只能在这里发发牢骚了,这个是转载的 ,发完睡觉! oracle sql语句 一.ORACLE的启动和关闭 .在单机环境下 ...
- DOS 命令大全
MS DOS 命令大全 一.基础命令 1 dir 无参数:查看当前所在目录的文件和文件夹. /s:查看当前目录已经其所有子目录的文件和文件夹. /a:查看包括隐含文件的所有文件. /ah:只显示出隐含 ...
- (转)QR二维码生成及原理
二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型:比如:字符,数字, ...
- MoveTo和MoveBy
cc.MoveTo是“移动到这里",而cc.MoveBy则是“相对于之前点再移动”,通俗一点就是说这里需要两个坐标pos1(x1,y1),pos2(x2,y2). 如果是cc.MoveTo的 ...
- asp.net mvc 4 编译错误 CS0012
CS0012: The type 'System.Data.Objects.DataClasses.EntityObject' is defined in an assembly that is no ...
- 安装完 MySQL 后必须调整的 10 项配置(转)
英文原文:10 MySQL settings to tune after installation 译文原文:安装完 MySQL 后必须调整的 10 项配置 当我们被人雇来监测MySQL性能时,人们希 ...