我发现了很多关于如何使用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}
有人可以告诉我如何解决这个问题吗?