Репозиторий против DAO (снова)

В общем, эта история не имеет значения, но только для объяснения кода ниже:

Сервер обрабатывает пользователей и группы пользователей. Группы пользователей могут "обнаруживать" места - в данный момент эти места поступают исключительно из API Google Адресов.


Текущая реализация


В настоящее время у меня много JpaRepository объекты, которые я называю Repository, в моем слое обслуживания. Я подчеркиваю "Репозиторий", потому что в предложенном ниже решении они будут понижены до DAO.

Однако, что мне не нравится в моем текущем коде, а также причина моего вопроса здесь - это количество репозиториев, которые можно найти в UserGroupService,

@Service
public class UserGroupService {

    private final static Logger LOGGER = LogManager.getLogger(UserGroupService.class);

    @Autowired
    private UserGroupRepository userGroupRepository;

    @Autowired
    private UserGroupPlaceRepository userGroupPlaceRepository;

    @Autowired
    private PlaceRepository placeRepository;

    @Autowired
    private GooglePlaceRepository googlePlaceRepository;

    @Autowired
    private GooglePlaces googlePlaces;

    public UserGroupService() {
    }

    @Transactional
    public void discoverPlaces(Long groupId) {

        final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null);

        if (userGroup == null) {
            throw new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
        }

        List<PlacesSearchResult> allPlaces = this.googlePlaces.findPlaces(
                userGroup.getLatitude(),
                userGroup.getLongitude(),
                userGroup.getSearchRadius());

        allPlaces.forEach(googlePlaceResult -> {

            GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);

            if (googlePlace != null) {
                return;
            }

            Place place = new Place();
            place.setLatitude(googlePlaceResult.geometry.location.lat);
            place.setLongitude(googlePlaceResult.geometry.location.lng);
            place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
            place.setName(googlePlaceResult.name);
            place.setVicinity(googlePlaceResult.vicinity);

            place = this.placeRepository.save(place);

            UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new UserGroupPlace.UserGroupPlaceId();
            userGroupPlaceId.setUserGroup(userGroup);
            userGroupPlaceId.setPlace(place);

            UserGroupPlace userGroupPlace = new UserGroupPlace();
            userGroupPlace.setUserGroupPlaceId(userGroupPlaceId);

            this.userGroupPlaceRepository.save(userGroupPlace);

            googlePlace = new GooglePlace();
            googlePlace.setPlace(place);
            googlePlace.setGooglePlaceId(googlePlaceResult.placeId);

            this.googlePlaceRepository.save(googlePlace);
        });
    }
}

Решение, которое не работает


Что могло бы сделать этот код намного проще и могло бы решить эту проблему, было бы @Inheritance:

@Entity
@Table(name = "place")
@Inheritance(strategy InheritanceType.JOINED)
public class Place { /* .. */ }

@Entity
@Table(name = "google_place")
public class GooglePlace extends Place { /* .. */ }

Тем не менее, это не вариант, потому что тогда я не могу иметь PlaceRepository который экономит только место. Hibernate, похоже, не нравится.,


Мое предложение


Я думаю, что моя путаница начинается с имен, которые использует Spring. Например JpaRepository - Я не уверен, действительно ли это "правильное" имя. Потому что, насколько я понял, эти объекты на самом деле работают как объекты доступа к данным (DAO). Я думаю, что это должно выглядеть примерно так:

public interface PlaceDao extends JpaRepository<Place, Long> {
}

public interface GooglePlaceDao extends JpaRepository<Place, Long> {
}

@Repository
public class GooglePlaceRepository {

    @Autowired
    private PlaceDao placeDao;

    @Autowired
    private GooglePlaceDao googlePlaceDao;

    public List<GooglePlace> findByGroupId(Long groupId) {
    // ..
    }

    public void save(GooglePlace googlePlace) {
    // ..
    }

    public void saveAll(List<GooglePlace> googlePlaces) {
    // ..
    }
}

@Service
public class UserGroupService {

    @Autowired
    private GooglePlaceRepository googlePlaceRepository;

    @Autowired
    private UserGroupRepository userGroupRepository;

    @Transactional
    public void discoverPlaces(Long groupId) {

    final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null)
        .orElseThrow(throw new EntityNotFoundException(String.format("User group with id %s not found.", groupId)));


    List<PlacesSearchResult> fetched = this.googlePlaces.findPlaces(
            userGroup.getLatitude(),
            userGroup.getLongitude(),
            userGroup.getSearchRadius());

    // Either do the mapping here or let GooglePlaces return 
    // List<GooglePlace> instead of List<PlacesSearchResult>

    List<GooglePlace> places = fetched.stream().map(googlePlaceResult -> {
        GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);

        if (googlePlace != null) {
            return googlePlace;
        }

        Place place = new Place();
        place.setLatitude(googlePlaceResult.geometry.location.lat);
        place.setLongitude(googlePlaceResult.geometry.location.lng);
        place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
        place.setName(googlePlaceResult.name);
        place.setVicinity(googlePlaceResult.vicinity);
        googlePlace = new GooglePlace();
        googlePlace.setPlace(place);
        googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
        return googlePlace;
    }).collect(Collectors.toList());

    this.googlePlaceRepository.saveAll(places);        

    // Add places to group..
    }

}

Резюме


Я хотел бы знать, что я не вижу. Я борюсь с фреймворком или моя модель данных не имеет смысла, и именно поэтому я сталкиваюсь с этим? Или у меня все еще есть проблемы с использованием двух шаблонов "Репозиторий" и "DAO"?

Как реализовать это?

2 ответа

Я бы сказал, что вы правы, что в вашем сервисе слишком много зависимостей репозитория. Лично я стараюсь сохранить количество @Autowired зависимости до минимума, и я пытаюсь использовать хранилище только в одном сервисе и раскрыть его функциональность более высокого уровня через этот сервис. В нашей компании мы называем этот суверенитет данных (по-немецки: Datenhoheit), и его цель - обеспечить, чтобы в приложении было только одно место, где эти объекты были изменены.

Из того, что я понимаю из вашего кода, я хотел бы представить PlacesService который имеет все зависимости от PlaceRepository, GooglePlaceRepository а также GooglePlaces, Если вы считаете, что Сервис - это не то имя, которое вы можете назвать PlacesDao, отметьте это с Весной @Component аннотировать и внедрять все хранилища, которые по определению являются коллекциями вещей

@Component
public class PlacesDao {

    @Autowired
    private PlaceRepository placeRepository;

    @Autowired
    private GooglePlaceRepository googlePlaceRepository;

Эта служба /DAO может предложить API findPlacesForGroup(userGroup) а также createNewPlace(...) и, таким образом, сделать вашу петлю меньше и элегантнее.

На заметку: вы можете объединить свои первые четыре строки в одну. Java Optionals поддерживают orElseThrow() метод:

UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() -> 
     new EntityNotFoundException(String.format("User group with id %s not found.", groupId));

Я думаю, что foreach не выглядит хорошим подходом для меня. Вы делаете многое для одной функции. Я бы переориентировал это на стандартный цикл.

        Place place = new Place();
        place.setLatitude(googlePlaceResult.geometry.location.lat);
        place.setLongitude(googlePlaceResult.geometry.location.lng);
        place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
        place.setName(googlePlaceResult.name);
        place.setVicinity(googlePlaceResult.vicinity);

        place = this.placeRepository.save(place);

Эта часть легко может быть методом в службе.

        UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new 
        UserGroupPlace.UserGroupPlaceId();
        userGroupPlaceId.setUserGroup(userGroup);
        userGroupPlaceId.setPlace(place);

        UserGroupPlace userGroupPlace = new UserGroupPlace();
        userGroupPlace.setUserGroupPlaceId(userGroupPlaceId);

        this.userGroupPlaceRepository.save(userGroupPlace);

Эта часть также.

        googlePlace = new GooglePlace();
        googlePlace.setPlace(place);
        googlePlace.setGooglePlaceId(googlePlaceResult.placeId);

        this.googlePlaceRepository.save(googlePlace);

И эта часть: я не понимаю, почему вы это делаете. Вы можете просто обновить экземпляр googlePlace, загруженный из репозитория. Hibernate/ Сделки делают все остальное за вас.

Другие вопросы по тегам