Недавно я хотел смоделировать мерчанта, который, как и многие вещи в модели предметной области, имел адрес. Я думал, что имеет смысл, что адрес был встроен в продавца. Причины:
- У Продавца не было жизненного цикла. Торговец умирает, так должен адрес.
- Он принадлежал только одному и только одному Торговцу
Совершенно очевидно, что это были композиционные отношения.
Теперь возможно моделировать композиционные отношения в 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
|
@Immutableclass 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 в Дублине . |