I have the entity classes below. When a user first signs up, only the username and password are supplied, so the list of accounts (think profiles) is empty. Later, when they add an account, the user object is updated in the client, passed to the server, and then entityManager.merge(user) is called. When the user is merged, the account is added 6 times to the database and the address supplied is added three times. I'm not sure why. I would like the account to be added once and only one address to be added. Any ideas on what may be happening?
@Entity
public class User 开发者_开发问答implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private int id;
@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name="user_accounts")
private List<Account> accounts;
//...getters and setters ...
}
@Entity
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private long id;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="address")
private Address address;
//...getters and setters...
}
@Entity
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private int id;
@Column(name="street")
private String street;
@Column(name="city")
private String city;
@Column(name="state")
private String state;
@Column(name="zip")
private String zip;
//...getters and setters...
}
This is a known issue with using merge where the collections are lists. Unfortunately now real fix yet: HHH-5855
Have you tried to:
persist(address)
account.setAddress(address)
persist(account)
user.setAccount(account)
merge(user)
I think because address and account have generated id and you specify cascade cause this problem.
My solution to this issue was to add an additional function to the controller which would update the row with a native SQL statement. Since my code updated part or the key (long story, but surprisingly worked amazingly well) I had to make sure I was not looking for the record based on the new values in the pojo. here is the code:
public void editSQLUpdate(Reportinfo reportinfo) throws NonexistentEntityException, Exception {
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
String qry = "UPDATE `boeaudit`.`reportinfo` "
+ "SET "
+ "`author` = '" + reportinfo.getAuthor() + "',"
+ "`db_account` = '" + reportinfo.getDbAccount() + "',"
+ "`db_schema_name` = '" + reportinfo.getDbSchemaName() + "',"
+ "`descriptions` = '" + reportinfo.getDescriptions() + "',"
+ "`DLL` = '" + reportinfo.getDll() + "',"
+ "`parent_folder` = " + reportinfo.getParentFolder() + ","
+ "`path` = '" + reportinfo.getPath() + "',"
+ "`report_title` = '" + reportinfo.getReportTitle() + "',"
+ "`report_id` = " + reportinfo.getReportinfoPK().getReportId() + ","
+ "`env` = " + reportinfo.getReportinfoPK().getEnv() + ","
+ "`db_server` = '" + reportinfo.getReportinfoPK().getDbServer() + "',"
+ "`seq` = " + reportinfo.getReportinfoPK().getSeq()
+ " WHERE `report_id` = " + reportinfo.getReportinfoPK().getReportId()
+ " AND `env` = " + reportinfo.getReportinfoPK().getEnv()
+ " AND `db_server` = '-'" //this is the initial value of the record and the update value differs, so if we pass the new value the record will not be found. ;)
+ " AND `seq` = "+ reportinfo.getReportinfoPK().getSeq();
Query nq = em.createNativeQuery(qry);
int outcome = nq.executeUpdate(); //not doing anything with outcome, but should be used to determine the result of the operation...
em.getTransaction().commit();
} catch (Exception ex) {
String msg = ex.getLocalizedMessage();
if (msg == null || msg.length() == 0) {
ReportinfoPK id = reportinfo.getReportinfoPK();
if (findReportinfo(id) == null) {
throw new NonexistentEntityException("The reportinfo with id " + id + " no longer exists.");
}
}
throw ex;
} finally {
if (em != null) {
em.close();
}
}
}
精彩评论