Hi Guys I have a problem with Hibernate when trying to write an array of object to the db. Essential I have an object built from a web service query. This object 'response' can have contain a maximum of ten 'unpaid items', my problem arises when I try to persist these.
Entity:
#Entity
#Table(name="TABLE_NAME")
public class AccountDetailsRROutput implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String payeename;
private String typeunpd;
private BigDecimal unpdamt;
#Column(name="TRANSACTION_ID")
private long transactionId;
public AccountDetailsRROutput() {
super();
}
// plus all get/sets
}
//================================================================
// Populate the output for the repeating rows table
// which can contain a maximum of 10 unpaid items
//===============================================================
AccountDetailsRROutput outputRRTable[] = new AccountDetailsRROutput[response.getLineItems().length];
LOGGER.debug(METHOD_NAME, "Loop through the line items");
for (int i = 0; i < response.getLineItems().length; i++) {
//================================================================
// Ensure that we have an item so we don't write an empty row
//================================================================
if (response.getLineItems()[i].getTypeunpd() == null || response.getLineItems()[i].getTypeunpd() == "") {
LOGGER.debug(METHOD_NAME, "No unpaid item entry so break out of the the loop");
break;
}
else {
LOGGER.debug(METHOD_NAME, "We've got an unpaid item so add the details to the DB");
outputRRTable[i] = new AccountDetailsRROutput();
outputRRTable[i].setTransactionId(iTransactionID);
outputRRTable[i].setTypeunpd(response.getLineItems()[i].getTypeunpd());
outputRRTable[i].setPayeename(response.getLineItems()[i].getPayeeName());
outputRRTable[i].setUnpdAmt(response.getLineItems()[i].getUnpdAmt());
//================================================================
// Persist the output list DB object
//================================================================
LOGGER.debug(METHOD_NAME, "Persist repeating rows table DB object for line item: " + (i+1));
em_i.persist(outputRRTable[i]);
}
}
LOGGER.debug(METHOD_NAME, "Finished persisting repeating rows table DB object");
em_i.flush();
When I try this I get the following error:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
I can get around this my changing emi.persist to emi.merge but it is only writing one element to the db. There can be duplicate records in this table and there is no pk.
You probably have more than one item with the same payeename. Try defining another id (for example an id composed of payeename and transactionID).
As per your hibernate mapping payeename is your primary key which means there can be only one entry per payeename in your database, the exception says NonUniqueObjectException which indicate that you are trying to persist another row with the same primary key.
Solution:
Before inserting, make sure there is no entry in the database with the primary key.
If the primary key already exist instead of insert a new record, do an update.
Hope it helps.
Related
I have a two Tables in Maximo 7.5.
Table A: has attribute WORKORDERNUM, EXPECTEDTIME and FINISHTIME in table A.
Table B: has attribute WORKNUM and STATUS in table B.
What I want to do is:
if FINISHTIME > EXPECTEDTIME then update STATUS in table B as "NOTGOOD" otherwise do nothing.
I have created a CronTask for that which will be running every five minutes.
Now I can think of is two approaches.
1. To loop through all Table A. Inside the loop perform a database query for Table B each time.
Here is the sample code:
MboSetRemote TableA = mxs.getMboSet("TABLEA", ui);
MboSetRemote TableB = mxs.getMboSet("TABLEB", ui);
TableA.setWhere("FINISHTIME > EXPECTEDTIME");
TableA.reset();
TableB.setWhere("");
TableB.reset();
MboSet TableARow = null;
MboSet TableBRow = null;
//now it will give a list of entries. Which needs to be matched with Table B and values be updated in Table B STATUS.
while ((TableARow = TableA.getMbo(i)) != null)
{
int A = TableA.getString("WONUM");
while((TableBRow = TableB.getMbo(i)) != null)
int B = TableB.getString("WONUM");
if (A == B){
//set SATUS etc}
}
TableB.save();
TableA.save();
2. To loop through all Table A. Inside the loop perform Compare the values for Table B each time.
MboSetRemote TableA = mxs.getMboSet("TABLEA", ui);
MboSetRemote TableB = mxs.getMboSet("TABLEB", ui);
TableA.setWhere("FINISHTIME > EXPECTEDTIME");
TableA.reset();
MboSet TableARow = null;
//now it will give a list of entries. Which needs to be matched with Table B and values be updated in Table B STATUS.
while ((TableARow = TableA.getMbo(i)) != null)
{
TableB.setWhere("WONUM= TABLEA.WONUM");
TableB.reset();
//set SATUS etc
TableB.save();
}
TableA.save();
Which one is the better and more cost effective?
Any other suggestions?
Automation scripts are fun to write and use, but they aren't always the best tool for the job. In this case, I would use
an Escalation to search TableA for FINISHTIME > EXPECTEDTIME
a Relationship in Database Configuration from TableA to TableB where wonum = :wonum and siteid = :siteid
an Action based on TableA that uses the above Relationship in standard notation to set the status in TableB
The main benefits of this approach over the ones presented are upgradeability and supportability. Upgradeability, because no code is involved that can be deprecated and because all configurations are upgradeable, and supportability because IBM supports configurations but not customizations. (In the case of Automation Scripts, your ability to write them is supported, but your code, itself, is not. It is the same with Relationships in Database Configuration.)
The most cost effective thing to do here would not be to use a Crontask every 5 minutes, but to perform a check just when every record is changed. This would be much much more efficient.
Make two custom field classes, one for each of the date fields, and attach the to the fields on database configuration.
The one on EXPECTEDTIME should be something like this:
public class CustFldExpectedTime extends MboValueAdapter {
public CustFldExpectedTime(MboValue mbv) throws MXException {
super(mbv);
}
#Override
public void action() throws MXException, RemoteException {
super.action();
MboValue mv = getMboValue();
MboRemote mbo = mv.getMbo();
Date expectedTime = mv.getDate();
Date finishTime = mbo.getDate("FINISHTIME");
if(finishTime.after(expectedTime)) {
// first argument is the name of on-the-fly relationship
// second argument is the name of the table your relationship is pointing to
// third argument is the relationship where clause
MboSetRemote tableBSet = mbo.getMboSet("$TABLEB", "TABLEB", ":WORKORDERNUM = WORKNUM");
if(!tableBSet.isEmpty()) {
MboRemote tableB = tableBSet.getMbo(0);
tableB.setValue("STATUS", "NOTGOOD");
}
}
}
}
I would like to save two entities with the following relationship:
#Entity
public class Synonyme {
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="ID", nullable = false)
private List<Term> terms = new ArrayList<>();
}
#Entity
public class Term {
private String word;
public static createTerm(String word, Synonyme newSyn, Long someothercriteria) {
Term term = new Term();
...
newSyn.addTerm(term);
}
}
And I want to save them like this:
Synonyme newSyn= Synonyme.createSynonyme ();
entityManager.persist(newSyn);
for(String word : words) {
Term term = Term.createTerm(word, newSyn, 0L );
}
entityManager.flush();
These codes work fine in Junit-Test (The Junit-Test uses an in-memory database of spring) but if I use them in the real database I got the following exception:
Caused by: javax.persistence.PersistenceException: org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 2; expected: 1
I don't understand this exception. What does it mean and what I could do?
(too long for comment, so adding an answer)
You should update the question with the code of Synonyme.createSynonyme() and Term.createTerm().
Your #OneToMany mapping is strange, that could be the cause of the problem (check out the documentation for collection mappings). In this #OneToMany the target table (Term) contains foreign key to source table (Synonyme). That said, ID is probably not the column you want, but something like SYNONYME_ID. You can also post your table structure so anyone interested can have the full picture. Once the mapping looks OK, it will be easier to look for the solution to the problem, if it doesn't get resolved when mappings are fixed.
That usually means you defined a primary key in hibernate but not on the actual DBMS.
And now there are 2 rows with the same primary key ("actual row count 2, expected 1") so hibernate cannot even perform a simple non-update query (!).
To fix, delete in DBMS the duplicated row or change hibernate key definition on that table.
Why is my persistent object returning transient objects when fetching via a relationship?
ObjectContext context = BaseContext.getThreadObjectContext();
// Delete some employee schedules
List<EmployeeSchedule> employeeSchedules = this.getEmployeeSchedules();
for (EmployeeSchedule employeeSchedule : employeeSchedules) {
context.deleteObject(employeeSchedule);
}
// Add new schedules
for(int i = 0; i < someCondition; i++) {
EmployeeSchedule employeeSchedule = context.newObject(EmployeeSchedule.class);
addToEmployeeSchedules(employeeSchedule);
}
context.commitChanges();
List<EmployeeSchedule> es = getEmployeeSchedules(); // returns transient objects
It is inserting the data correctly into the database. Would this be an issue with stale data in the cache?
I'm answering my own question in case someone else get tripped up by this in the future.
I have a many-to-many relationship.
Employee - EmployeeSchedule - Schedule
According to the delete rules here: http://cayenne.apache.org/docs/3.0/delete-rules.html, I set the fields employee_id and schedule_id in the EmployeeSchedule to Nullify rule on delete.
I also had to configure the join table EmployeeSchedule by making employee_id and schedule_id primary keys in the Modeler and checking the "to Dep PK" checkbox in the employee and schedule dbEntity.
Relevant links: http://objectstyle.org/cayenne/lists/cayenne-user/2004/02/0017.html
http://grokbase.com/t/cayenne/user/085d70sysk/to-dep-checkbox-was-one-to-many-problem
I have two entities: Questionnaire and QuestionnaireTime. Questionnaire's id is a foreign key in QuestionnaireTime. So the relationship in my QuestionnaireTime entity looks like this:
#JoinColumn(name = "questionnaireid", referencedColumnName = "id")
#ManyToOne(cascade = CascadeType.PERSIST)
private Questionnaire questionnaireid;
So what I'm trying to do is to add multiple QuestionnaireTime records for one Questionnaire. If I remove the CascadeType.PERSIST part in my relationship, my persist is not done. And when I use cascade, I get several new records in my main table Questionnaire and that's not what I want.
For example when I want to add three QuestionnaireTime's for a certain Questionnaire, the three records are inserted in my QuestionnaireTime table but also 3+1 records are added in Questionnaire.
If you need more explanation. This is my managed bean, the part that I'm trying to add multiple QuestionnaireTime records in one Questionnaire:
NB - current is my Questionnaire object
else if (current.getType().equals("frequent")) {
int iteration = 1;
currentQuestionnaireTime = new QuestionnaireTime();
if (!selectDateList.isEmpty()) {
for (String insertedDate : selectDateList) {
currentQuestionnaireTime.setId(0);
currentQuestionnaireTime.setQuestionnaireid(current);
getEjbQuestionnaireTimeFacade().create(currentQuestionnaireTime);
iteration++;
}
}
}
try {
getFacade().create(current); // my Questionnaire facade
} catch (EJBException ejbe) {
ejbe.getCause();
}
A few things,
questionnaireid - this is a very bad field name, questionnaire would make sense.
currentQuestionnaireTime.setId(0); - you should not be changing the id of an existing object, instead create a new object
getEjbQuestionnaireTimeFacade().create() - what does this do? If you need the reference to the current, then the current should be persisted first. If you EJB remote? If it is, then either make it local, or ensure you use merge() not persist(), as you new object has a reference to a detached object. Or find the reference in the current persistence context.
I'm struggling with a problem that seems way too easy:
Setup is two Entities with a Many-To-One Relationsship in Hibernate 3:
#Entity
class M {
private N n;
#ManyToOne(fetch = FetchType.LAZY)
public N getN() { return n; }
public void setN(N n) { this.n = n; }
}
#Entity
class N {
private List<M> ms = new ArrayList<M>();
#OneToMany(mappedBy="n")
public List<M> getMs() { return ms; }
public void setMs(List<M> ms) { this.ms = ms; }
}
Easy enough. In my application, I have a list of Ms that either have an N or don't. This list is the input for a h:dataTable that shows different column content depending on whether the FK is null or not. But when I test m.getN() != null this causes hibernate to load N. How can I avoid this?
Edit: this is actually an error of mine, as pointed out by JBNizet in the comments. To at least make this useful to someone and keep with the layout above, I've rephrased the question to "How do I get the foreign key column value of a dependent Hibernate Entity without fetching the full entity?" as suggested by Aaron Digulla.
Edit 2: Turns out the new question is a duplicate of this one: How can I prevent Hibernate fetching joined entities when I access only the foreign key id? - so, close vote?
Create a projection mapping which contains M or several fields of M and e.g. id of N
Your query might liook sopething like
select new com.my.ProjectionObject(m, m.n.id) from M m where ...
How do you expect Hibernate to tell you something it doesn't know? Without loading the entity, Hibernate has no way to know whether it (still) exists.
If you step outside the Hibernate "entity mapper" box, you can query the database directly and, for example, count the number of Ns for your M.