使用Spring Data MongoDB在事务中的两个不同ReactiveMongoRepository中调用方法? [英] Calling methods in two different ReactiveMongoRepository's in a transaction using Spring Data MongoDB?

查看:926
本文介绍了使用Spring Data MongoDB在事务中的两个不同ReactiveMongoRepository中调用方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Spring Data MongoDB中使用反应式编程模型时,可以执行如下事务:

When using the reactive programming model with Spring Data MongoDB it's possible to execute transactions like this:

Mono<DeleteResult> result = template.inTransaction()                                      
    .execute(action -> action.remove(query(where("id").is("step-1")), Step.class)); 

但是Spring Data MongoDB也支持反应性存储库",例如:

But Spring Data MongoDB also has support for "reactive repositories", for example:

public interface PersonRepository extends ReactiveMongoRepository<Person, String>

  Flux<Person> findByLocationNear(Point location, Distance distance);
}

public interface CarRepository extends ReactiveMongoRepository<Car, String>

  Flux<Car> findByYear(int year);
}

我的问题是,假设您拥有ReactiveMongoRepository,可以以某种方式利用MongoDB事务,例如在同一个事务中同时插入PersonCar(在这种情况下使用PersonRepositoryCarRepository)?如果是这样,您该怎么做?

My question is, given that you have ReactiveMongoRepository's, can you somehow leverage MongoDB transactions and e.g. insert both a Person and Car in the same transaction (using PersonRepository and CarRepository in the case)? If so, how do you do this?

推荐答案

我也一直在努力寻找Mongo DB& Reactive风格的 Transactional 支持的解决方案.春季靴子

I had also been trying hard to find solution for the Transactional support in Reactive style of Mongo DB & Spring Boot

但是幸运的是我自己想通了.尽管google的一些功能也无济于事,但这些都是无反应的.

But luckily I figured it myself. Though few of the things from google were also helpful but those were non reactive.

  • 您需要使用 ReactiveMongoTransactionManager 以及 ReactiveMongoDatabaseFactory ,最后大部分信息,还共享相同的代码仓库

  • You need to use ReactiveMongoTransactionManager along with ReactiveMongoDatabaseFactory, most of the details at the end, also sharing the code repo for the same

为使mongo数据库支持事务,我们需要确保数据库应以副本方式运行 .

For getting the mongo db to support the Transactions we need to make sure that the DB should be running in replica mode.

为什么我们需要那个?因为否则您会收到类似以下的错误:-

此客户端连接到的MongoDB集群不支持会话

相同的说明如下:-

  1. 使用docker-compose.yml运行基于docker-compose的mongo数据库服务器,如下所示:-

version: "3"
services:
    mongo:
        hostname: mongo
        container_name: localmongo_docker
        image: mongo
        expose:
          - 27017
        ports:
          - 27017:27017
        restart: always
        entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0" ]
        volumes:
          - ./mongodata:/data/db # need to create a docker volume named as mongodata first

  1. 显示图像后,执行命令(此处 localmongo_docker 是容器的名称):-
  1. After the image comes up, execute the command(here localmongo_docker is the name of the container):-

docker exec -it localmongo_docker mongo

  1. 复制并粘贴以下命令并执行

rs.initiate(
   {
     _id : 'rs0',
     members: [
       { _id : 0, host : "mongo:27017" }
     ]
   }
 )

  1. 然后通过输入退出
  2. 退出执行
  1. And then exit the execution by entering exit

重要 -代码仓库可以在我的github上找到-该代码的重要说明如下:-

Important notes for the code are as below:-

  • MongoConfiguration class in the config package is the important part to make the transactions working, link to the configuration class is here
  • Main part is the Bean

@Bean
ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory dbFactory) {
    return new ReactiveMongoTransactionManager(dbFactory);
}

  • 要检查代码的事务性要求的工作情况,可以检查服务包如果链接不适用于某人,则共享代码:-

    Code shared in case the links do not work for someone:-

    配置和Bean内部

    @Configuration
    public class MongoConfiguration extends AbstractMongoClientConfiguration {
    
        @Autowired
        private MongoProperties mongoProperties;
    
        @Bean
        ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory dbFactory) {
            return new ReactiveMongoTransactionManager(dbFactory);
        }
    
        @Override
        protected String getDatabaseName() {
            return mongoProperties.getDatabase();
        }
    
        @Override
        public MongoClient mongoClient() {
            return MongoClients.create(mongoProperties.getUri());
        }
    }
    

    application.properties (与mongo db有关)

    application.properties (related to mongo db)

    spring.data.mongodb.database=mongo
    spring.data.mongodb.uri=mongodb://localhost:27017/mongo?replicaSet=rs0
    

    文档类

    角色类别

    @Getter
    @Setter
    @Accessors(chain = true)
    @Document(collection = "roles")
    @TypeAlias("role")
    public class Role implements Persistable<String> {
    
        @Id
        private String id;
    
        @Field("role_name")
        @Indexed(unique = true)
        private String role;
    
        @CreatedDate
        private ZonedDateTime created;
    
        @LastModifiedDate
        private ZonedDateTime updated;
    
        private Boolean deleted;
    
        private Boolean enabled;
    
        @Override
        @JsonIgnore
        public boolean isNew() {
            if(getCreated() == null)
                return true;
            else
                return false;
        }
    }
    

    用户类别

    @Getter
    @Setter
    @Accessors(chain = true)
    @Document(collection = "users")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @TypeAlias("user")
    public class User implements Persistable<String> {
    
        @Id()
        private String id;
    
        @Field("username")
        @Indexed(unique = true)
        @JsonProperty("username")
        private String userName;
    
        @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
        private String password;
    
        @CreatedDate
        private ZonedDateTime created;
    
        @LastModifiedDate
        private ZonedDateTime updated;
    
        private Boolean deleted;
    
        private Boolean enabled;
    
        @DBRef(lazy = true)
        @JsonProperty("roles")
        private List<Role> roles = new ArrayList();
    
        @Override
        @JsonIgnore
        public boolean isNew() {
            if(getCreated() == null)
                return true;
            else
                return false;
        }
    }
    

    UserProfile类

    @Getter
    @Setter
    @Accessors(chain = true)
    @Document(collection = "user_profiles")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @TypeAlias("user_profile")
    public class UserProfile implements Persistable<String> {
    
        @Id
        private String id;
    
        @Indexed(unique = true)
        private String mobile;
    
        @Indexed(unique = true)
        private String email;
    
        private String address;
    
        private String firstName;
    
        private String lastName;
    
        @DBRef
        private User user;
    
        @CreatedDate
        private ZonedDateTime created;
    
        @LastModifiedDate
        private ZonedDateTime updated;
    
        private Boolean deleted;
    
        private Boolean enabled;
    
        @Override
        @JsonIgnore
        public boolean isNew() {
            if(getCreated() == null)
                return true;
            else
                return false;
        }
    
    }
    

    ReactiveMongoRepository接口

    RoleRepository

    public interface RoleRepository extends ReactiveMongoRepository<Role, String> {
    
        Mono<Role> findByRole(String role);
    
        Flux<Role> findAllByRoleIn(List<String> roles);
    
    }
    

    UserRepository

    public interface UserRepository extends ReactiveMongoRepository<User, String> {
    
        Mono<User> findByUserName(String userName);
    
    }
    

    UserProfileRepository

    public interface UserProfileRepository extends ReactiveMongoRepository<UserProfile, String> {
    }
    

    用户服务类,需要在此处创建自己的RuntimeException类,这里是AppRuntimeException类,我一直在使用

    The User Service Class Need to create your own RuntimeException Class here, here it is AppRuntimeException Class, I had been using

    @Slf4j
    @Service
    public class UserService {
    
        @Autowired
        private RoleRepository roleRepository;
    
        @Autowired
        private UserRepository userRepository;
    
        @Autowired
        private UserProfileRepository userProfileRepository;
    
        @Transactional
        public Mono<UserProfile> saveUserAndItsProfile(final UserRequest userRequest) {
    
            Mono<Role> roleMono = roleRepository.findByRole("USER");
    
            Mono<User> userMono = roleMono.flatMap(r -> {
                User user = new User()
                        .setUserName(userRequest.getUsername())
                        .setPassword(userRequest.getPassword());
                user.setRoles(Arrays.asList(r));
                return userRepository.save(user);
            }).onErrorResume(ex -> {
                log.error(ex.getMessage());
                if(ex instanceof DuplicateKeyException) {
                    String errorMessage = "The user with the username '"+userRequest.getUsername()+"' already exists";
                    log.error(errorMessage);
                    return Mono.error(new AppRuntimeException(errorMessage, ErrorCodes.CONFLICT, ex));
                }
                return Mono.error(new AppRuntimeException(ex.getMessage(), ErrorCodes.INTERNAL_SERVER_ERROR, ex));
            });
    
            Mono<UserProfile> userProfileMono = userMono.flatMap(u -> {
                UserProfile userProfile = new UserProfile()
                        .setAddress(userRequest.getAddress())
                        .setEmail(userRequest.getEmail())
                        .setMobile(userRequest.getMobile())
                        .setUser(u);
                return userProfileRepository.save(userProfile);
            }).onErrorResume(ex -> {
                log.error(ex.getMessage());
                if(ex instanceof DuplicateKeyException) {
                    String errorMessage = "The user with the profile mobile'"+userRequest.getMobile()+"' and/or - email '"+userRequest.getEmail()+"' already exists";
                    log.error(errorMessage);
                    return Mono.error(new AppRuntimeException(errorMessage, ErrorCodes.CONFLICT, ex));
                }
                return Mono.error(new AppRuntimeException(ex.getMessage(), ErrorCodes.INTERNAL_SERVER_ERROR, ex));
            });
    
            return userProfileMono;
    
        }
    
    }
    

    控制器和模型类

    UserRequest 模型类

    @Getter
    @Setter
    @Accessors(chain = true)
    @Slf4j
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class UserRequest {
    
        private String username;
        private String password;
        private String mobile;
        private String email;
        private String address;
        private String firstName;
        private String lastName;
    
    }
    

    UserProfileApisController

    @Slf4j
    @RestController
    @RequestMapping("/apis/user/profile")
    public class UserProfileApisController {
    
        @Autowired
        private UserService userService;
    
        @PostMapping
        public Mono<UserProfile> saveUserProfile(final @RequestBody UserRequest userRequest) {
            return userService.saveUserAndItsProfile(userRequest);
        }
    
    }
    

    这篇关于使用Spring Data MongoDB在事务中的两个不同ReactiveMongoRepository中调用方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆