Недавно я хотел смоделировать мерчанта, который, как и многие вещи в модели предметной области, имел адрес. Я думал, что имеет смысл, что адрес был встроен в продавца. Причины:
- У Продавца не было жизненного цикла. Торговец умирает, так должен адрес.
- Он принадлежал только одному и только одному Торговцу
Совершенно очевидно, что это были композиционные отношения.
Теперь возможно моделировать композиционные отношения в 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 | @ImmutableclassAddress {    privatefinalString city;    privatefinalString country;    privatefinalString state;    privatefinalString street1;    privatefinalString street2;    privatefinalString street3;    privatefinalString zip;    publicString getCity() {        returncity;    }    publicString getCountry() {        returncountry;    }    publicString getZip() {        returnzip;    }    publicString getState() {        returnstate;    }    publicString getStreet1() {        returnstreet1;    }    publicString getStreet2() {        returnstreet2;    }    publicString getStreet3() {        returnstreet3;    }} | 
Вот мой объект 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 | classAddressUserType implementsUserType {    publicint[] 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[]    }    publicClass getReturnedClass() {        returnAddress.class;    }    publicObject nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throwsSQLException {        assertnames.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        returncity == null&& v == null? null: newGAddress(city: city, country: country, state: state, street1: street1, street2: street2,  street3: street3, zip: zip);    }    voidnullSafeSet(java.sql.PreparedStatement st, java.lang.Object value, intindex, org.hibernate.engine.spi.SessionImplementor session) throwsorg.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{            finalAddress 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    publicbooleanisMutable() {        returnfalse;    }    @Override    publicbooleanequals(Object x, Object y) throwsHibernateException {        // for now        returnx.equals(y);    }    @Override    publicinthashCode(Object x) throwsHibernateException {        assert(x != null);        returnx.hashCode();    }    @Override    publicObject deepCopy(Object value) throwsHibernateException {        returnvalue;    }    @Override    publicObject replace(Object original, Object target, Object owner)            throwsHibernateException {        returnoriginal;    }    @Override    publicSerializable disassemble(Object value) throwsHibernateException {        return(Serializable) value;    }    @Override    publicObject assemble(Serializable cached, Object owner)            throwsHibernateException {        returncached;    }    publicClass returnedClass() {        returnAddress.class;    }} | 
А вот мой торговец, у которого есть адрес.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | classMerchant {    UUID id;    String color;    String displayName;    //...    //...    Address address        staticmapping = {        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 в Дублине . |