R2DBC TransientDataAccessResourceException:ID 为 [...] 的行不存在
R2DBC TransientDataAccessResourceException: Row with Id [...] does not exist
作为 R2DBC 学习过程的一部分,我有一个创建相关实体作为创建主要实体的一部分的复杂场景。我正在尝试创建一个 Film 实体及其相关实体。
我的要求是这样的
{
"title" : "Star Wars: The Rise Of Skywalker",
"description": "In the riveting conclusion of the landmark Skywalker saga, new legends will be born-and the final battle for freedom is yet to come.",
"releaseYear": "2019",
"language": "English",
"rentalDuration": 7,
"rentalRate": "4.99",
"length": 165,
"replacementCost": "14.99",
"rating": "PG",
"specialFeaturesList": [ "SciFi", "Star Wars"],
"filmCategory": "Sci-Fi",
"actors":[
{
"firstName":"Daisy",
"lastName": "Ridley"
},
{
"firstName":"Carrie",
"lastName":"Fisher"
},
{
"firstName": "Oscar",
"lastName": "Isaac"
},
{
"firstName": "Adam",
"lastName": "Driver"
},
{
"firstName": "Mark",
"lastName": "Hamill"
},
{
"firstName": "John",
"lastName": "Boyega"
}
]
}
下面是我的逻辑和代码
/**
* Logic :
* Transaction start
* -- find Language or insert
* -- find Flim by (Title,release year and languageId) or insert
* -- foreach( actor)
* ---- find or create actor
* ---- find or create film_actor ( film id, actor)
* -- end
* -- for category
* ---- find category or insert
* ---- find film_category or insert
* -- end
* -- Map data
* Transaction end
*
* @param filmRequest
* @return Mono<Pair < Boolean, FilmModel>>
*/
代码:
@Override
public Mono<Pair<Boolean, FilmModel>> create(FilmRequest filmRequest) {
return Mono.just(filmRequest)
.flatMap(this::getOrCreateLanguage)
.flatMap(languageInput -> getOrCreateFilm(filmRequest, languageInput.getLanguageId()))
.flatMap(filmInput ->
Mono.zip(Mono.just(filmInput),
Flux.fromIterable(filmRequest.getActors())
.flatMap(this::getOrCreateActor)
.doOnNext(actorInput -> getOrCreateFilmActor(filmInput.getSecond().getFilmId(), actorInput.getActorId()))
.collectList(),
Mono.just(filmRequest.getFilmCategory())
.flatMap(this::getOrCreateCategory)
.flatMap(category -> getOrCreateFilmCategory(filmInput.getSecond().getFilmId(), category.getCategoryId()))
))
.map(tuple -> {
FilmModel filmModel = GenericMapper.INSTANCE.filmToFilmModel(tuple.getT1().getSecond());
List<ActorModel> actorModelList = tuple.getT2().stream().map(GenericMapper.INSTANCE::actorToActorModel).collect(Collectors.toList());
filmModel.setActors(actorModelList);
return Pair.of(tuple.getT1().getFirst(), filmModel);
}).as(operator::transactional);
}
这就是我遇到的失败:
TransientDataAccessResourceException: Failed to update table
[film_category]. Row with Id [1006] does not exist.
https://gist.github.com/harryalto/d91e653ca1054038b766169142737f87
电影实体 ID 未保留,因此相关插入失败。
我解决了这个问题。主要问题是对于 film_category 和 film_actor,主要是外键的组合(即来自电影的 film_id,来自类别的 category_id/来自演员的 actor_id ).我不得不为这两个东西更改存储库的定义
@Repository
public interface FilmActorRepository extends
ReactiveCrudRepository<FilmActor, FilmActorPK> {
@Query("select * from film_actor where film_id = :film_id")
Flux<FilmActor> findAllByFilmId(@Param("film_id") final Long filmId);
@Query("select * from film_actor where film_id = :film_id and actor_id = :actor_id limit 1")
Mono<FilmActor> findByFilmIdAndActorId(@Param("film_id") final Long filmId, @Param("actor_id") final Long actorId);
@Modifying
@Query("insert into film_actor (film_id, actor_id, last_update) values (:#{#filmActor.filmId}, :#{#filmActor.actorId}, now()) on conflict DO NOTHING")
Mono<FilmActor> save(final FilmActor filmActor);
}
其中 FilmActorPK 定义为
@Value
@Builder
public class FilmActorPK implements Serializable {
private long filmId;
private long actorId;
}
我对 FilmCategoryRepository 做了类似的练习
@Repository
public interface FilmCategoryRepository extends
ReactiveCrudRepository<FilmCategory, FilmCategoryPK> {
@Query("select * from film_category where category_id = :category_id and film_id = :film_id limit 1")
Mono<FilmCategory> findFirstByCategoryIdAndFilmId(@Param("category_id") Long categoryId, @Param("film_id") Long filmId);
@Query("select * from film_category where film_id = :film_id limit 1")
Mono<FilmCategory> findFirstByFilmId(@Param("film_id") Long filmId);
@Modifying
@Query(
"insert into film_category (film_id, category_id, last_update)
values (:#{#filmCategory.filmId}, :#{#filmCategory.categoryId},
now()) on conflict DO NOTHING"
)
Mono<FilmCategory> save(final FilmCategory filmCategory);
}
其中 FilmCategoryPK 定义为
@Value
@Builder
public class FilmCategoryPK implements Serializable {
private long filmId;
private long categoryId;
}
这个想法来自这个问题here
这解决了我的问题。我知道 R2DBC 有不同的理念,有些事情可以在 R2DBC 中完成,使其与 JPA 的语法一样,有些则不能。但我真的很喜欢 R2DBC,因为我在学习中做了更多的练习。
作为 R2DBC 学习过程的一部分,我有一个创建相关实体作为创建主要实体的一部分的复杂场景。我正在尝试创建一个 Film 实体及其相关实体。
我的要求是这样的
{
"title" : "Star Wars: The Rise Of Skywalker",
"description": "In the riveting conclusion of the landmark Skywalker saga, new legends will be born-and the final battle for freedom is yet to come.",
"releaseYear": "2019",
"language": "English",
"rentalDuration": 7,
"rentalRate": "4.99",
"length": 165,
"replacementCost": "14.99",
"rating": "PG",
"specialFeaturesList": [ "SciFi", "Star Wars"],
"filmCategory": "Sci-Fi",
"actors":[
{
"firstName":"Daisy",
"lastName": "Ridley"
},
{
"firstName":"Carrie",
"lastName":"Fisher"
},
{
"firstName": "Oscar",
"lastName": "Isaac"
},
{
"firstName": "Adam",
"lastName": "Driver"
},
{
"firstName": "Mark",
"lastName": "Hamill"
},
{
"firstName": "John",
"lastName": "Boyega"
}
]
}
下面是我的逻辑和代码
/**
* Logic :
* Transaction start
* -- find Language or insert
* -- find Flim by (Title,release year and languageId) or insert
* -- foreach( actor)
* ---- find or create actor
* ---- find or create film_actor ( film id, actor)
* -- end
* -- for category
* ---- find category or insert
* ---- find film_category or insert
* -- end
* -- Map data
* Transaction end
*
* @param filmRequest
* @return Mono<Pair < Boolean, FilmModel>>
*/
代码:
@Override
public Mono<Pair<Boolean, FilmModel>> create(FilmRequest filmRequest) {
return Mono.just(filmRequest)
.flatMap(this::getOrCreateLanguage)
.flatMap(languageInput -> getOrCreateFilm(filmRequest, languageInput.getLanguageId()))
.flatMap(filmInput ->
Mono.zip(Mono.just(filmInput),
Flux.fromIterable(filmRequest.getActors())
.flatMap(this::getOrCreateActor)
.doOnNext(actorInput -> getOrCreateFilmActor(filmInput.getSecond().getFilmId(), actorInput.getActorId()))
.collectList(),
Mono.just(filmRequest.getFilmCategory())
.flatMap(this::getOrCreateCategory)
.flatMap(category -> getOrCreateFilmCategory(filmInput.getSecond().getFilmId(), category.getCategoryId()))
))
.map(tuple -> {
FilmModel filmModel = GenericMapper.INSTANCE.filmToFilmModel(tuple.getT1().getSecond());
List<ActorModel> actorModelList = tuple.getT2().stream().map(GenericMapper.INSTANCE::actorToActorModel).collect(Collectors.toList());
filmModel.setActors(actorModelList);
return Pair.of(tuple.getT1().getFirst(), filmModel);
}).as(operator::transactional);
}
这就是我遇到的失败:
TransientDataAccessResourceException: Failed to update table [film_category]. Row with Id [1006] does not exist.
https://gist.github.com/harryalto/d91e653ca1054038b766169142737f87
电影实体 ID 未保留,因此相关插入失败。
我解决了这个问题。主要问题是对于 film_category 和 film_actor,主要是外键的组合(即来自电影的 film_id,来自类别的 category_id/来自演员的 actor_id ).我不得不为这两个东西更改存储库的定义
@Repository
public interface FilmActorRepository extends
ReactiveCrudRepository<FilmActor, FilmActorPK> {
@Query("select * from film_actor where film_id = :film_id")
Flux<FilmActor> findAllByFilmId(@Param("film_id") final Long filmId);
@Query("select * from film_actor where film_id = :film_id and actor_id = :actor_id limit 1")
Mono<FilmActor> findByFilmIdAndActorId(@Param("film_id") final Long filmId, @Param("actor_id") final Long actorId);
@Modifying
@Query("insert into film_actor (film_id, actor_id, last_update) values (:#{#filmActor.filmId}, :#{#filmActor.actorId}, now()) on conflict DO NOTHING")
Mono<FilmActor> save(final FilmActor filmActor);
}
其中 FilmActorPK 定义为
@Value
@Builder
public class FilmActorPK implements Serializable {
private long filmId;
private long actorId;
}
我对 FilmCategoryRepository 做了类似的练习
@Repository
public interface FilmCategoryRepository extends
ReactiveCrudRepository<FilmCategory, FilmCategoryPK> {
@Query("select * from film_category where category_id = :category_id and film_id = :film_id limit 1")
Mono<FilmCategory> findFirstByCategoryIdAndFilmId(@Param("category_id") Long categoryId, @Param("film_id") Long filmId);
@Query("select * from film_category where film_id = :film_id limit 1")
Mono<FilmCategory> findFirstByFilmId(@Param("film_id") Long filmId);
@Modifying
@Query(
"insert into film_category (film_id, category_id, last_update)
values (:#{#filmCategory.filmId}, :#{#filmCategory.categoryId},
now()) on conflict DO NOTHING"
)
Mono<FilmCategory> save(final FilmCategory filmCategory);
}
其中 FilmCategoryPK 定义为
@Value
@Builder
public class FilmCategoryPK implements Serializable {
private long filmId;
private long categoryId;
}
这个想法来自这个问题here
这解决了我的问题。我知道 R2DBC 有不同的理念,有些事情可以在 R2DBC 中完成,使其与 JPA 的语法一样,有些则不能。但我真的很喜欢 R2DBC,因为我在学习中做了更多的练习。