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");
}
}
}
}
Related
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.
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.
I have class A and class B.
Now class A has Set of B.
class A{
Set<B> bset //
}
bset.size() can be > 100 or > 500 etc
Now i want to change one filed of all B which corresponds to A
Eg:update B set Bfield = x where id IN (1,2,3);
1)What will be the query in hibernate?
2)1,2,3 are ids of B (B.id).
3)How can i retrive all ids of B that corresponds to A in the format required by the query?
If I understood you correct
update B set b.field = :newValue where b.parent = :parentA
I have a model:
#Entity
class A {
#ManyToOne
B b;
int i;
}
#Entity
class B {
#OneToMany(mappedBy="b")
List< A > list = new ArrayList< A >();
}
Now in my EJB service I do:
void f(Aid,Bid){
A a = em.find(A.class, Aid);
B b = em.find(B.class, Bid);
a.setB(b);
}
This throw db constraint exception (INSERT INTO B VALUES... WITH SAME VALUES THAT ALREADY EXIST IN CURRENT B).
Why it's try to insert a new record to B while it's already exist in B table and the relationship is not PERSIST?
NOTES:
1 When i do after the last row:
em.merge(a);
It's work as well...???
2 When i do a.setI(1); without merge - it's changed in DB (like i thought it will)
THANKS!!!
It's a bidirectional relationship. You should always update both sides of the relations ships. Otherwise, if you don't directly have problems while persisting, you can easily corrupt your cache and face some stange bugs in your application.
So, if you have this:
A a = em.find(A.class, Aid);
B b = em.find(B.class, Bid);
a.setB(b);
You should add this right after:
List<A> aList = b.getList();
if (!aList.contains(b)) {
aList.add(b);
}
2 When i do a.setI(1); without merge -
it's changed in DB (like i thought it
will)
If you're using an entityManager in an ejb (which I seems you do regarding your tags), it's normal that changes to managed entities are persisted. Was that te question?
Also, you should make sure that entities identity, equals() and hashcode() are defined correctly (maybe it is, but you didn't mention it).
I'm converting a legacy iBatis implementation to Hibernate, and for backwards compatibility purposes need to present counts of an object's collections rather than the collections themselves. The original query was:
select A.*, ( select count(*) from B where B.A_id = A.id ) as B_count from A;
and b_count would be presented in the response. I'd like to be able to do the same without lazy-loading A's collection of B's for each query result.
Any ideas or suggestions?
The best method seems to be using a Hibernate formula, mapped to the getter and setter of my BCount attribute in the class A. My code:
public class A {
// ...
private long bCount;
// ...
#Formula( "(select count(*) from B where B.A_id = id" )
public long getBCount() {
return this.bCount;
}
public void setBCount( long bCount ) {
this.bCount = bCount;
}
}
Great thing about this method is that the count is returned in the same fetch to hydrate the initial object, and does not result in 1+N queries for collection query results!
You can use a projection.
The syntax for the row count is below:
Criteria crit = session.createCriteria(B.class);
crit.setProjection(Projections.rowCount());
List results = crit.list();
Edit: After re-reading, I think this may not be what you're asking for....
Hibernate filters are used to apply additional restrictions to query results (e.g. think of them as part of "where" clause), so they won't do what you want. You have two options here:
A) You can eagerly get collection of Bs for your A:
from A a left join fetch a.Bs b
If you do so, keep in mind that for queries that would return multiple As you may get duplicates in the result list (e.g. if you have 2 As and each of them has 3 Bs you'll get 6 results back). Wrap them in a set to ensure uniqueness.
B) Assuming you have an appropriate constructor for A, you can do the following:
select new A(a.field1, a.field2, ... a.fieldN, count(*) as B_count)
from A a left join a.Bs b
group by a.field1, a.field2, ... a.fieldN