事务管理在Spring Boot中不起作用

时间:2018-04-01 13:18:43

标签: hibernate spring-boot spring-data-jpa spring-transactions

我有两个实体,即公司用户。一家公司可以有很多用户。因此,当我保存公司时,它还会在一个事务中创建一个用户。但是,如果由于某种原因导致用户插入失败,我希望公司的插入应该回滚,目前这种情况不会发生。

公司实体:

@Entity
public class Company {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @NotNull
    @Size(min = 4, max = 100)
    private String name;

    @Size(max = 500)
    private String description;

    @OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
    @JsonManagedReference
    private List<User> users = new ArrayList<>();

    public Company() {
    }

    ;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}

用户实体:

@Entity
public class User implements UserDetails {

    /**
   * 
   */
  private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(length = 50, unique = true)
    @NotNull
    @Size(min = 4, max = 50)
    private String username;

    @NotNull
    @Size(min = 8)
    private String password;

    @Column(length = 50)
    @NotNull
    @Size(min = 4, max = 50)
    private String firstName;

    @Column(length = 50)
    @NotNull
    @Size(min = 4, max = 50)
    private String lastName;

    @Column(length = 50, unique = true)
    @NotNull
    @Size(min = 4, max = 50)
    private String email;

    private Boolean enabled = true;

    @Temporal(TemporalType.TIMESTAMP)
    @UpdateTimestamp
    private Date lastPasswordReset;

    @Column(nullable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    private Date createdAt = new Date();

    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @UpdateTimestamp
    @LastModifiedDate
    private Date updatedAt = new Date();

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(
            name = "user_authority",
            joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "authority_id", referencedColumnName = "id")})
    private List<Authority> roles;

    @ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
    @JsonBackReference
    private Company company;

    @Transient
    @JsonIgnore
    private Collection<? extends GrantedAuthority> authorities;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    /**
     * Indicates whether the user's account has expired. An expired account cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user's account is valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * Indicates whether the user is locked or unlocked. A locked user cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * Indicates whether the user's credentials (password) has expired. Expired
     * credentials prevent authentication.
     *
     * @return <code>true</code> if the user's credentials are valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * Indicates whether the user is enabled or disabled. A disabled user cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
     */
    @Override
    public boolean isEnabled() {
        return true;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * Returns the authorities granted to the user. Cannot return <code>null</code>.
     *
     * @return the authorities, sorted by natural key (never <code>null</code>)
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (this.getRoles() != null) {
            return mapToGrantedAuthorities(this.getRoles());
        }
        return null;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        if (enabled == null) {
            enabled = false;
        }
        this.enabled = enabled;
    }

    public Date getLastPasswordReset() {
        return lastPasswordReset;
    }

    public void setLastPasswordReset(Date lastPasswordReset) {
        if (lastPasswordReset == null) {
            lastPasswordReset = new Date();
        }
        this.lastPasswordReset = lastPasswordReset;
    }

    public List<Authority> getRoles() {
        return roles;
    }

    public void setRoles(List<Authority> roles) {
        this.roles = roles;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    private List<GrantedAuthority> mapToGrantedAuthorities(List<Authority> authorities) {
        return authorities.stream()
                .map(authority -> new SimpleGrantedAuthority(authority.getName().name()))
                .collect(Collectors.toList());
    }
}

公司服务

@Service
public class CompanyService implements ICompanyService {

    @Autowired
    CompanyRepository companyRepository;

    @Autowired
    IUserService userService;

    @Override
    @Transactional
    public Company save(Company company) {
        try {
            userService.initUserFromCompany(company);
            return companyRepository.save(company);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

请求正文

 {
    "name" : "Evil Corp 2",
    "description" : "Bad company",
           "users" : [ 
                        {
                            "username" : "rambo",
                            "password" : "rambo_123",
                            "firstName" : "Slilvestor",
                            "lastName" : "Stelon",
                            "email" : "rambo@rambo.com"
                        }
                    ]
 }

因此,如果以某种方式,请求中传递的电子邮件已经存在,那么用户实体将不会被保存,因此在这种情况下我需要公司回滚。任何想法我怎么能实现这种行为?

1 个答案:

答案 0 :(得分:0)

有几种方法可以解决这个问题。我会建议你一个,如果用户在表中有相同的电子邮件,则抛出RuntimeException。在userService方法中,我猜测是initUserFromCompany方法,如果有的话,抛出RuntimeException。在save方法中,您应该这样做:

@Override
@Transactional(rollbackFor=Exception.class)
public Company save(Company company) {

  userService.initUserFromCompany(company);
  return companyRepository.save(company);
}

有几个与回滚相关的帖子和​​答案。您可以查看this以获取详细说明。