今天在開發中發現一個問題,原本想對一個VO對象的removed值賦值,而後去update一下這條記錄,一個最簡單的set方法,可是在調用時直接拋異常了。數據庫
1: public void setRemoved(Date removed) {
2: this.removed = removed;
3: }
當時很詫異,沒有想到這地方會出問題,後來看代碼才發現原來cs在這裏有攔截器,com.cloud.utils.db.UpdateBuilder#interceptide
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#makeChangeui
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。this
Attribute是cloudstack的基類GenericDaoBase中保存的對象屬性集合,是一個保存了數據表的列名以及列屬性的Map對象,這個對象經過Java JPA來進行持久化。spa
每個幹活的DAO對象都會繼承於GenericDaoBase,而且實現本身這類對象的接口,以下圖:對象
剛纔說的那個isUpdatable()是我的認爲cloudstack中有關數據庫權限作的很精巧的地方blog
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,數據庫表每一列可能的全部權限列表: