Ustawianie pola na podstawie nazwy

0

Witam,
piszę obecnie aplikację z wykorzystaniem Jacksona, Jersey oraz Hibernate. Encje JPA są od razu serializowane i zwracane bez zastosowania jakichś dodatkowych obiektów opakowujących.

Problem zaczyna się w momencie, gdy chcę te encje edytować. Mogę oczywiście odbierać automatycznie deserializowane encje

@POST
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response update(@PathParam("id") int id, User user) {...}

ale chciałbym umożliwić użytkownikowi przesyłanie tylko tych pól które chce modyfikować. W takim rozwiązaniu pozostałe pola dostaną wartość NULL i zapis obiektu do bazy nadpisze wartości (albo rzuci wyjątkiem, jeżeli kolumna jest niemodyfikowalna).

Drugim rozwiązaniem jest przesyłanie mapy parametrów

@POST
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response update(@PathParam("id") int id, Map<String, Object> properties) {...}

a następnie ręczne ich kopiowanie do obiektu User odobno pobranego z bazy. W tym rozwiązaniu problemem jest oczywiście konieczność ręcznej deserializacji obiektów do właściwego typu, oraz samo ustawienie pola na podstawie nazwy. Można to pewnie zrobić przy pomocy refleksji, ale ja wykorzystałem bibliotekę Commons BeanUtils i metodę BeanUtils.copyProperty(Object bean, String name, Object value). Takie rozwiązanie też ma swoje wady, bo wymaga znów pisania deserializatorów które już mam napisane dla Jacksona (fakt, to przeważnie parę linijek kodu tylko).

Pytanie, w jaki sposób najczęściej rozwiązujecie tego typu problemy w swoich aplikacjach? Jest może jakiś mechanizm 'scalenia' takich encji w JPA, osobiście nie bardzo sobie to wyobrażam, bo przecież NULLa mogę sam umieścić w polu?

1

Ja bym jednak skorzystał z DTO. Wtedy wysyłasz tylko takie dane, jakie potrzebuje aplikacja.
Odnośnie wartości nullowych, to możesz np przyjąć, że jeśli nie wysyłasz danej wartości to zawsze jest NULL. Jeśli zmieniasz jej wartość na null to nadajesz jej jakąś przyjętą specjalną wartość.

0

Z wysyłaniem tylko tych danych które chcę radzę sobie obecnie dzięki odpowiednim adnotacjom dla Jacksona. Co do samych DTO rozważałem i nadal rozważam ich zastosowanie, ale szczerze powiedziawszy nie bardzo widzę zaletę, gdzieś i tak ręcznie muszę kopiować każde pole po polu (chyba, że istnieją jakieś fajne mapery które robią to jakoś sensownie? Czy też jedynym wyjściem jest coś w tym stylu:

class UserMapper {
    static UserDTO toDTO(User u){
        UserDTO dto = new UserDTO();
        dto. name = u.name;
        dto.id = u.id;
        ...
        return dto;
    }
    static User toEntity(UserDTO dto){...}
}

a może odpowiednie konstruktory? Co do NULLi, to to jest wyjście, ale znów wymaga pisania sporej ilości ifów gdzieś. Nie ma na to jakiejś automatycznej metody?

1

Może coś takiego?

@Transactional
void update(User user) {
  User u = session.find(user.id); //pobieramy, gdyż encja musi być w stanie Managed
  BeanUtils.copyProperties(u, user, new String[] {"propertyNieDoZmiany1", "propertyNieDoZmiany2"});
  //Na koncu metody zmiany zostana spropagowane do bazy
}

http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/beans/BeanUtils.html#copyProperties%28java.lang.Object,%20java.lang.Object,%20java.lang.String[]%29

0

W zasadzie przypomina to metodę jaką opisałem w pierwszym poście, ale możliwość przekazania ignorowanych właściwości jest sporym plusem. Nie jest to jeszcze do końca to o co mi chodziło, ale jest już blisko (idealnie było by przekazać tablicę dozwolonych pól, ale widziałem podobną metodę w dokumentacji Springa). Przyjrzę się jutro dokładnie jak to wygląda i czy nie pociągnie mi tony zależności (na szczęście chyba nie).

1 użytkowników online, w tym zalogowanych: 0, gości: 1