Spring 存储库与DAO(再次)
一般来说,这个背景故事并不重要,只是为了解释下面的代码: 服务器处理用户和用户组。用户组能够“发现”地点——此时这些地点完全来自GooglePlacesAPISpring 存储库与DAO(再次),spring,web-services,server,abstraction-layer,Spring,Web Services,Server,Abstraction Layer,一般来说,这个背景故事并不重要,只是为了解释下面的代码: 服务器处理用户和用户组。用户组能够“发现”地点——此时这些地点完全来自GooglePlacesAPI 当前实施 目前,我的服务层中有很多JpaRepository对象,我称之为存储库。我之所以强调“存储库”,是因为在我下面提出的解决方案中,它们将降级为DAOs 然而,我不喜欢我当前的代码,也是我在这里提出问题的原因,是人们在UserGroupService中可以找到的存储库数量 @Service public class UserGr
当前实施
目前,我的服务层中有很多
JpaRepository
对象,我称之为存储库。我之所以强调“存储库”,是因为在我下面提出的解决方案中,它们将降级为DAOs
然而,我不喜欢我当前的代码,也是我在这里提出问题的原因,是人们在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);
});
}
}
但是,这不是一个选项,因为这样我就不能有一个只保存一个位置的PlaceRepository
我的建议
我想我的困惑始于Spring使用的名称。例如,
jpa positionary
-我不太确定这是否真的是“正确”的名称。因为据我所知,这些对象实际上就像数据访问对象(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..
}
}
公共接口PlaceDao扩展了JpaRepository{
}
公共接口GooglePlaceDao扩展了JpaRepository{
}
@存储库
公共类GooglePlaceRepository{
@自动连线
私人配售;
@自动连线
私有的GooglePlaceDao GooglePlaceDao;
公共列表findByGroupId(长groupId){
// ..
}
公共作废保存(Google Place Google Place){
// ..
}
public void saveAll(列出googlePlaces){
// ..
}
}
@服务
公共类UserGroupService{
@自动连线
私有GooglePlaceRepository GooglePlaceRepository;
@自动连线
私有UserGroupRepository UserGroupRepository;
@交易的
公共空间发现地点(长groupId){
final UserGroup UserGroup=this.userGroupRepository.findById(groupId).orElse(null)
.OrelsThrow(抛出新EntityNotFoundException(String.format(“未找到id为%s的用户组)”,groupId));
List fetched=this.googlePlaces.findPlaces(
userGroup.getLatitude(),
userGroup.getLongitude(),
userGroup.getSearchRadius());
//要么在这里进行映射,要么让GooglePlaces返回
//列表而不是列表
List places=fetched.stream().map(googlePlaceResult->{
GooglePlace GooglePlace=this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);
if(googlePlace!=null){
返回谷歌广场;
}
地点=新地点();
setLatitude(googlePlaceResult.geometry.location.lat);
setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(place.PlaceType.GOOGLE_place);
setName(googlePlaceResult.name);
place.setapproach(googlePlaceResult.approach);
googlePlace=新的googlePlace();
googlePlace.setPlace(place);
setGooglePlaceId(googlePlaceResult.placeId);
返回谷歌广场;
}).collect(Collectors.toList());
this.googlePlaceRepository.saveAll(地点);
//将位置添加到组。。
}
}
总结
我想知道我没有看到什么。我是在与框架抗争,还是我的数据模型毫无意义,这就是我发现自己在与框架抗争的原因?还是我对如何使用这两种模式“Repository”和“DAO”还有疑问
如何实现这一点?我认为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);
这部分:我不明白你为什么这么做。您可以只更新从repo加载的googlePlace实例。Hibernate/事务正在为您完成其余的工作。我认为您的服务中存在太多的存储库依赖项是正确的。就我个人而言,我尝试将
@Autowired
依赖项的数量保持在最低限度,并且我尝试仅在一个服务中使用存储库,并通过该服务公开其更高级别的功能。在我们公司,我们称之为数据主权(德语:Datenhoheit),其目的是确保在应用程序中只有一个地方可以修改这些实体
根据我从您的代码中了解到的情况,我将介绍一个places服务
,它与PlaceRepository
、GooglePlaceRepository
和GooglePlaces
具有所有依赖关系。如果您觉得服务不是正确的名称,您也可以将其称为PlacesDao
,用Spring@组件
注释标记它,并注入所有存储库,这些存储库根据定义是事物的集合
@Component
public class PlacesDao {
@Autowired
private PlaceRepository placeRepository;
@Autowired
private GooglePlaceRepository googlePlaceRepository;
此服务/DAO可以提供APIfindPlacesForGroup(userGroup)
和createNewPlace(…)
,从而使for循环更小、更优雅
旁注:您可以将前四行合并为一行。Java选项支持orelsetrow()
方法:
UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() ->
new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
更适合你。嗨!谢谢你的回答。所以问题是:我有不同类型的地方(另一个是
CUSTOM\u-PLACE
(CP))。因此,UserGroupPlace
可以显示CP和GOOGLE\u PLACE
(GP)。然而,GP需要额外的信息(placeId
),这意味着我必须编写一个新的Place
、UserGroupPlace
和一个GooglePlace
。我可以做的是创建一个UserGroupGooglePlaceDao
,它包装了功能,并通过确保数据库中正确存储此类位置来处理此类位置。但是,对于这种特殊类型,我还需要一个UserGroupCustomPlaceDao
UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() ->
new EntityNotFoundException(String.format("User group with id %s not found.", groupId));