Spring Boot JPA ManyToMany使用额外列删除和更新

时间:2017-02-26 21:55:05

标签: java mysql hibernate jpa spring-boot

我发现了很多关于如何使用JPA和Spring Boot处理额外列的ManyToMany关系的示例。例如:

https://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/

Jpa ManytoMany issue with Spring Boot

然而,我无法让他们中的任何一个工作。我可以创建条目,但我无法更新或删除它们。有人可以请我指出一个有效的例子,或者告诉我我的代码有什么问题吗?

以下是我想要完成的内容的概述。我有两个实体:用户和搜索。用户可以进行多次搜索,搜索可以拥有多个用户。另外,我想保留每个用户的搜索位置/顺序以及创建/更新的日期。

在我的MySQL数据库中,我创建了以下表格:

CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_mongo_id` VARCHAR(64) NOT NULL,
`company_mongo_id` VARCHAR(64) NOT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` TIMESTAMP NOT NULL DEFAULT now() on update now(),
PRIMARY KEY (`user_id`),
CONSTRAINT user_company UNIQUE (`user_mongo_id`, `company_mongo_id`)
);

CREATE TABLE `search` (
`search_id` int(11) NOT NULL AUTO_INCREMENT,
`search_mongo_id` VARCHAR(64) NOT NULL,
`type` VARCHAR(20) NOT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` TIMESTAMP NOT NULL DEFAULT now() on update now(),
PRIMARY KEY (`search_id`),
CONSTRAINT search_type UNIQUE (`search_mongo_id`, `type`)
);

CREATE TABLE `user_search` (
`user_id` int(11) NOT NULL,
`search_id` int(11) NOT NULL,
`position` int(11) NOT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` TIMESTAMP NOT NULL DEFAULT now() on update now(),
PRIMARY KEY (`user_id`, `search_id`),
FOREIGN KEY (`user_id`) REFERENCES user(`user_id`),
FOREIGN KEY (`search_id`) REFERENCES search(`search_id`),
CONSTRAINT user_position UNIQUE (`user_id`, `position`)
);

这是User.java:

@Entity
public class User implements java.io.Serializable {

@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name = "user_id", unique = true, nullable = false)
private long userId;
private String userMongoId;
private String companyMongoId;

@OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.user", cascade=CascadeType.ALL)
private Set<UserSearch> userSearches;

@Temporal(TemporalType.TIMESTAMP)
private Date created;

@Temporal(TemporalType.TIMESTAMP)
private Date updated;

public User() {}

public User(String userMongoId, String companyMongoId)
{
    this.userMongoId = userMongoId;
    this.companyMongoId = companyMongoId;
    this.userSearches = new HashSet<UserSearch>();
    this.created = this.updated = new Date();
}

<getters and setters for variables>

}

这是Search.java:

@Entity
public class Search implements java.io.Serializable {

@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name = "search_id", unique = true, nullable = false)
private long searchId;

private String searchMongoId;
private String type;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.search")
private Set<UserSearch> userSearches;

@Temporal(TemporalType.TIMESTAMP)
private Date created;

@Temporal(TemporalType.TIMESTAMP)
private Date updated;

public Search() {}

public Search(String searchMongoId, String type) {
    this.searchMongoId = searchMongoId;
    this.type = type;
    this.userSearches = new HashSet<UserSearch>();
    this.created = this.updated = new Date();
}

<getters and setters for variables>

}

这是UserSearch.java:

@Entity
@Table(name = "user_search")
@AssociationOverrides({
    @AssociationOverride(name = "pk.user",
            joinColumns = @JoinColumn(name = "user_id")),
    @AssociationOverride(name = "pk.search",
            joinColumns = @JoinColumn(name = "search_id")) })
public class UserSearch implements Serializable, Comparable<UserSearch> {

private UserSearchId pk = new UserSearchId();

private int position;

@Temporal(TemporalType.TIMESTAMP)
private Date created;

@Temporal(TemporalType.TIMESTAMP)
private Date updated;

public UserSearch() {
    this.created = this.updated = new Date();
}

@EmbeddedId
public UserSearchId getPk() {
    return pk;
}

public void setPk(UserSearchId pk) {
    this.pk = pk;
}

@Transient
public User getUser() {
    return getPk().getUser();
}

public void setUser(User user) {
    getPk().setUser(user);
}

@Transient
public Search getSearch() {
    return getPk().getSearch();
}

public void setSearch(Search search) {
    getPk().setSearch(search);
}

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}

public Date getCreated() {
    return created;
}

public void setUpdated(Date updated) {
    this.updated = updated;
}

public Date getUpdated() {
    return updated;
}

public void setCreated(Date created) {
    this.created = created;
}

public boolean equals(Object o) {
    if (this == o)
        return true;
    if (o == null || getClass() != o.getClass())
        return false;

    UserSearch that = (UserSearch) o;

    if (getPk() != null ? !getPk().equals(that.getPk())
            : that.getPk() != null)
        return false;

    return true;
}

public int hashCode() {
    return (getPk() != null ? getPk().hashCode() : 0);
}

@Override
public int compareTo( UserSearch other ) {
    if (other != null) {
        return getPosition() - other.getPosition();
    }
    return 0;
}

}

这是UserSearchId.java;

@Embeddable
public class UserSearchId implements java.io.Serializable {

private User user;
private Search search;

@ManyToOne
public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

@ManyToOne
public Search getSearch() {
    return search;
}

public void setSearch(Search search) {
    this.search = search;
}

public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    UserSearchId that = (UserSearchId) o;

    if (user != null ? !user.equals(that.user) : that.user != null) return false;
    if (search != null ? !search.equals(that.search) : that.search != null)
        return false;

    return true;
}

public int hashCode() {
    int result;
    result = (user != null ? user.hashCode() : 0);
    result = 31 * result + (search != null ? search.hashCode() : 0);
    return result;
}

}

我为Users对象创建了一个PagingAndSortingRepository:

public interface UserRepository extends PagingAndSortingRepository<User, Long>, JpaSpecificationExecutor<User> {

User findByUserMongoIdAndCompanyMongoId(String userId, String companyId);
}

我为搜索对象创建了一个PagingAndSortingRepository:

public interface SearchRepository extends PagingAndSortingRepository<Search, Long>, JpaSpecificationExecutor<Search> {

Search findBySearchMongoIdAndType(String searchMongoId, String type);
}

我可以成功创建用户,搜索和UserSearch对象:

User chuck = new User("Chuck", "Norris");
userrepo.save(chuck);

Search search = new Search("search1", "type1");
searchRepo.save(search);

UserSearch userSearch = new UserSearch();
userSearch.setUser(chuck);
userSearch.setSearch(search);
userSearch.setPosition(1);
chuck.getUserSearches().add(userSearch);
userrepo.save(chuck);

但是,如果我尝试删除或修改UserSearch对象,我会收到StackOverflowError。例如:

chuck.getUserSearches().clear();
search.getUserSearches().clear();
userrepo.save(chuck);

或:

userSearch.setPosition(5);
userrepo.save(chuck);

我一遍又一遍地执行这些查询:

{"@timestamp":"2017-02-26T13:43:13.605-08:00","message":"select user0_.user_id as user_id1_2_1_, user0_.company_mongo_id as company_2_2_1_, user0_.created as created3_2_1_, user0_.updated as updated4_2_1_, user0_.user_mongo_id as user_mon5_2_1_, usersearch1_.user_id as user_id4_3_3_, usersearch1_.search_id as search_i5_3_3_, usersearch1_.search_id as search_i5_3_0_, usersearch1_.user_id as user_id4_3_0_, usersearch1_.created as created1_3_0_, usersearch1_.position as position2_3_0_, usersearch1_.updated as updated3_3_0_ from user user0_ left outer join user_search usersearch1_ on user0_.user_id=usersearch1_.user_id where user0_.user_id=?","logger_name":"org.hibernate.SQL","thread_name":"main","level":"DEBUG","level_value":10000,"HOSTNAME":"ITs-MacBook-Pro-2.local","appid":"mobile-search-service","instanceid":0}

有人可以告诉我如何解决这个问题吗?

0 个答案:

没有答案