存储库与 DAO(再次) [英] Repository vs. DAO (again)
问题描述
一般来说,这个背景故事无关紧要,只是为了解释下面的代码:
In general this back-story does not matter but just to explain the code below:
服务器处理用户和用户组.用户组能够发现"地点 - 目前这些地点完全来自 Google Places API.
The server handles users and user groups. User groups are able to "discover" places - at this point in time these places are coming exclusively from the Google Places API.
目前,我的服务层中有很多 JpaRepository
对象,我称之为 Repository.我强调Repository",因为在我下面提出的解决方案中,它们会降级为 DAO.
Currently, I have a lot of JpaRepository
objects, which I call Repository, in my Service Layer. I am stressing "Repository" because in my proposed solution below, they'd be downgraded to DAOs.
但是,我不喜欢当前代码中的内容,也是我在这里提出问题的原因,是可以在 UserGroupService
中找到的存储库数量.
However, what I do not like in my current code, and also the reason for my question here, is the amount of repositories one can find in the 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
:
A Solution That Does Not Work
What could make this code a lot simpler and had the potential to resolve this mess up there, would be @Inheritance
:
@Entity
@Table(name = "place")
@Inheritance(strategy InheritanceType.JOINED)
public class Place { /* .. */ }
@Entity
@Table(name = "google_place")
public class GooglePlace extends Place { /* .. */ }
然而,这不是一个选项,因为那样我就不能有一个 PlaceRepository
来保存一个地方.Hibernate 似乎不喜欢它.一>.
However, this is not an option because then I cannot have a PlaceRepository
which saves just a place. Hibernate does not seem to like it..
我认为我的困惑始于 Spring 使用的名称.例如.JpaRepository
- 我不太确定这是否真的是正确的"名称.因为据我所知,这些对象实际上像数据访问对象 (DAO) 一样工作.我认为它实际上应该是这样的:
I think my confusion starts with the names that Spring is using. E.g. JpaRepository
- I am not so sure if this is actually "the right" name. Because as far as I understood, these objects actually work like data access objects (DAOs). I think it should actually look something like this:
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..
}
}
<小时>
总结
<小时>我想知道我没有看到什么.我是在与框架作斗争,还是我的数据模型没有意义,这就是为什么我发现自己在为此苦苦挣扎?或者我是否仍然对Repository"和DAO"这两种模式应该如何使用有疑问?
Summary
I would like to know what I don't see. Am I fighting the framework, or does my data model not make sense and this is why I find myself struggling with this? Or am I still having issues on how the two patterns "Repository" and "DAO" are supposed to be used?
如何实现这一点?
推荐答案
我认为您的服务中存在太多存储库依赖项是正确的.就我个人而言,我尝试将 @Autowired
依赖项的数量保持在最低限度,并且我尝试仅在一项服务中使用存储库,并通过该服务公开其更高级别的功能.在我们公司,我们称之为数据主权(德语:Datenhoheit),其目的是确保应用程序中只有一个地方可以修改这些实体.
I would say you are correct that there are too many repository dependencies in your service. Personally, I try to keep the number of @Autowired
dependencies to a minimum and I try to use a repository only in one service and expose its higher level functionality via that service. At our company we call that data sovereignty (in German: Datenhoheit) and its purpose is to ensure that there is only one place in the application where those entities are modified.
根据我从您的代码中了解到的,我将引入一个 PlacesService
,它具有 PlaceRepository
、GooglePlaceRepository
和 的所有依赖项GooglePlaces
.如果您觉得 Service 不是正确的名称,您也可以将其称为 PlacesDao
,用 Spring @Component
注释标记它并注入所有Repositories,顾名思义就是事物的集合
From what I understand from your code I would introduce a PlacesService
which has all the Dependencies to the PlaceRepository
, GooglePlaceRepository
and GooglePlaces
. If you feel like Service is not the right name you could also call it the PlacesDao
, mark it with a Spring @Component
annotation and inject all the Repositories, which are by definition collections of things
@Component
public class PlacesDao {
@Autowired
private PlaceRepository placeRepository;
@Autowired
private GooglePlaceRepository googlePlaceRepository;
此服务/DAO 可以提供 API findPlacesForGroup(userGroup)
和 createNewPlace(...)
,从而使您的 for 循环更小更优雅.
This service/DAO could offer an API findPlacesForGroup(userGroup)
and createNewPlace(...)
and thus making your for Loop smaller and more elegant.
附带说明:您可以将前四行合并为一行.Java Optionals 支持 orElseThrow()
方法:
On a side note: you can merge your first four lines into just one. Java Optionals support a orElseThrow()
method:
UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() ->
new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
这篇关于存储库与 DAO(再次)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!