5

一般来说,这个背景故事并不重要,只是为了解释下面的代码:

服务器处理用户和用户组。用户组能够“发现”地点——此时这些地点完全来自 Google Places API。


当前实施


目前,我的服务层中有很多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”这两种模式有疑问?

将如何实现这一点?

4

2 回答 2

2

我会说你的服务中有太多的存储库依赖项是正确的。就个人而言,我尝试将@Autowired依赖项的数量保持在最低限度,并尝试仅在一项服务中使用存储库,并通过该服务公开其更高级别的功能。在我们公司,我们称之为数据主权(德语:Datenhoheit),其目的是确保应用程序中只有一个地方可以修改这些实体。

根据我对您的代码的理解,我将介绍一个PlacesService具有所有依赖项的PlaceRepository,GooglePlaceRepositoryGooglePlaces. 如果您觉得Service不是正确的名称,您也可以将其称为 the PlacesDao,用 Spring@Component注释标记它并注入所有Repositories,根据定义,它们是事物的集合

@Component
public class PlacesDao {

    @Autowired
    private PlaceRepository placeRepository;

    @Autowired
    private GooglePlaceRepository googlePlaceRepository;

该服务/DAO 可以提供 API findPlacesForGroup(userGroup)createNewPlace(...)从而使您的 for Loop 更小、更优雅。

附带说明:您可以将前四行合并为一行。Java Optionals 支持一个orElseThrow()方法:

UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() -> 
     new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
于 2018-04-12T06:42:34.463 回答
0

我认为 foreach 对我来说看起来不是一个好方法。您为一个功能的单一职责做了很多事情。我会将其重构为标准的 for 循环。

        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/Transactions 为您完成其余的工作。

于 2018-04-10T08:48:32.217 回答