Недавно я хотел смоделировать мерчанта, который, как и многие вещи в модели предметной области, имел адрес. Я думал, что имеет смысл, что адрес был встроен в продавца. Причины:
- У Продавца не было жизненного цикла. Торговец умирает, так должен адрес.
- Он принадлежал только одному и только одному Торговцу
Совершенно очевидно, что это были композиционные отношения.
Теперь возможно моделировать композиционные отношения в GORM. Смотрите здесь Однако этот подход сопровождается оговоркой, что адрес должен быть объектом GORM. Я не хотел, чтобы Адрес был объектом GORM, потому что объекты GORM мощны в Grails. Со всеми своими динамическими поисковиками и API-интерфейсами GORM они по сути как DAO на стероидах. Если разработчик получает в свои руки, он может сделать много вещей (не всегда хорошие вещи). Я не хотел или не нуждался в этом. Кроме того, хорошая архитектура мешает разработчикам делать ошибки, когда они работают под давлением на высоких скоростях. Это означает, что когда вы принимаете проектные решения, вам нужно думать о силе, которую вы должны дать, дать и дать.
Имея это в виду, я изучил возможность создания пользовательского типа для Address. Это была бы просто структура данных, которая смоделировала бы адрес, могла бы быть повторно использована за пределами Продавца (таким образом, продвигая согласованность и снова продвигая, таким образом, продвигая хороший дизайн) и не придавая силы GORM. В документации GORM есть некоторая документация для пользовательских типов, но не полный рабочий пример. Я взглянул на некоторые примеры Hibernate, а затем сумел собрать их воедино и приступить к работе.
Вот мой адрес объекта.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
@Immutable class Address { private final String city; private final String country; private final String state; private final String street1; private final String street2; private final String street3; private final String zip; public String getCity() { return city; } public String getCountry() { return country; } public String getZip() { return zip; } public String getState() { return state; } public String getStreet1() { return street1; } public String getStreet2() { return street2; } public String getStreet3() { return street3; } } |
Вот мой объект AddressUserType:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
class AddressUserType implements UserType { public int [] sqlTypes() { return [ StringType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType() ] as int [] } public Class getReturnedClass() { return Address. class ; } public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLException { assert names.length == 7 ; log.debug( ">>mullSafeGet(name=${names}" ) String city = StringType.INSTANCE.get(rs, names[ 0 ], session); // already handles null check String country = StringType.INSTANCE.get(rs, names[ 1 ], session ); // already handles null check String state = StringType.INSTANCE.get(rs, names[ 2 ], session ); // already handles null check String street1 = StringType.INSTANCE.get(rs, names[ 3 ], session ); // already handles null check String street2 = StringType.INSTANCE.get(rs, names[ 4 ], session ); // already handles null check String street3 = StringType.INSTANCE.get(rs, names[ 5 ], session ); // already handles null check String zip = StringType.INSTANCE.get(rs, names[ 6 ], session ); // already handles null check return city == null && v == null ? null : new GAddress(city: city, country: country, state: state, street1: street1, street2: street2, street3: street3, zip: zip); } void nullSafeSet(java.sql.PreparedStatement st, java.lang.Object value, int index, org.hibernate.engine.spi.SessionImplementor session) throws org.hibernate.HibernateException, java.sql.SQLException { if ( value == null ) { StringType.INSTANCE.set( st, null , index ); StringType.INSTANCE.set( st, null , index+ 1 ); StringType.INSTANCE.set( st, null , index+ 2 ); StringType.INSTANCE.set( st, null , index+ 3 ); StringType.INSTANCE.set( st, null , index+ 4 ); StringType.INSTANCE.set( st, null , index+ 5 ); StringType.INSTANCE.set( st, null , index+ 6 ); } else { final Address address = (Address) value; StringType.INSTANCE.set( st, address.getCity(), index,session ); StringType.INSTANCE.set( st, address.getCountry(), index+ 1 ,session); StringType.INSTANCE.set( st, address.getState(), index+ 2 ,session); StringType.INSTANCE.set( st, address.getStreet1(), index+ 3 ,session); StringType.INSTANCE.set( st, address.getStreet2(), index+ 4 ,session); StringType.INSTANCE.set( st, address.getStreet3(), index+ 5 ,session); StringType.INSTANCE.set( st, address.getZip(), index+ 6 ,session); } } @Override public boolean isMutable() { return false ; } @Override public boolean equals(Object x, Object y) throws HibernateException { // for now return x.equals(y); } @Override public int hashCode(Object x) throws HibernateException { assert (x != null ); return x.hashCode(); } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } public Class returnedClass() { return Address. class ; } } |
А вот мой торговец, у которого есть адрес.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Merchant { UUID id; String color; String displayName; //... //... Address address static mapping = { address type: AddressUserType, { column name: "city" column name: "country" column name: "zip" column name: "state" column name: "street1" column name: "street2" column name: "street3" } } } |
Как указано, при таком подходе структура данных Address может использоваться в других объектах GORM. До следующего раза береги себя.
Ссылка: | Пользовательский пользователь вводит GORM от нашего партнера JCG Алекса Стейвли в блоге Techlin в Дублине . |