Einsatz des ODS 5 API im Projekt „Dämpferprüfstand“ bei der Audi AG Erfahrungsbericht 1. 2. 3. 4. 5. Aufgabenstellung Performantes Lesen von Daten mit DB Views IstecODS: Effiziente Entwicklung mit Hilfsklassen Datenkonsistenz mit Hilfe von DB Triggern Ausblick © 06/2005 Aufgabenstellung „Es ist ein Software-System zu entwickeln, welches die effiziente Abwicklung, Datenhaltung und Auswertung von verschiedenartigen Dämpferprüfungen ermöglicht.“ Einsatz von: • ZEUS (Audi Komponenten Container) • ASAM ODS mit ODS 5 API • Oracle 9i • DIAdem (X-Frame) © 06/2005 Aufgabenstellung Aufgaben: • Verwalten und Suchen von: • Aufträgen und Versuchen • Fahrzeugen für Dauerlaufprüfungen • Prüflingstypen und Prüflingen • Kennlinien • Generieren der Messprogramme für den Prüfstand • Ergebnisablage in ODS • Auswertung mit DIAdem © 06/2005 ODS-Modell © 06/2005 Komplexe Recherche und Ablage © 06/2005 Performantes Lesen von Daten mit DB Views Aufgabe: Suchen von Aufträgen Problem: Information ist über 15 Applikationselemente verteilt. Die Suche ist sehr zeitaufwendig. Das Auflösen von Relationen und das Auslesen von Attributen bedeuten viele CORBA Roundtrips. Lösung: Verwendung von Datenbank-Views, via SQL-Joins. Im Applikationsmodell werden Views z.B. als AoAny Tabellen bekannt gemacht. © 06/2005 Performantes Lesen von Daten mit DB Views Vorteile: • Konform zu ODS, da nur zusätzliche Tabellen definiert werden müssen • Ermöglicht Angabe von Suchkriterien über alle Attribute • Instanzieren von vielen Java Objekten mit nur einer Abfrage Nachteile: • Pflege des Datenmodells ist aufwendiger • Nutzung durch generische Applikationen ist nur bedingt möglich © 06/2005 IstecODS: Effiziente Entwicklung mit Hilfsklassen ODS Abfrage ohne Hilfsklasse: QueryStructure query = new QueryStructure(); ApplicationElement ae = getViewApplicationElement(); (Fortsetzung) ApplicationAttribute[] appAttrArr = ae.getAttributes("*"); ApplicationElement[] aeRelated = ae.getAllRelatedElements(); query.anuSeq = aidNUID; AIDNameUnitId[] aidNUID = new AIDNameUnitId[appAttrArr.length + aeRelated.length]; TS_Union value = new TS_Union(); value.longVal(pAuftragId); int index = 0; for (int i = 0; i < appAttrArr.length; ++i) { ApplicationAttribute appAttr = appAttrArr[i]; String attrName = appAttr.getName(); SelValue selVal = new SelValue(); aidNUID[index] = new AIDNameUnitId(); aidNUID[index].attr = new AIDName(); aidNUID[index].attr.aid = ae.getId(); aidNUID[index].attr.aaName = attrName; aidNUID[index].unitId = new T_LONGLONG(); ++index; } ApplicationStructure as = ae.getApplicationStructure(); for (int i = 0; i < aeRelated.length; ++i) { ApplicationRelation rel[] = as.getRelations(ae, aeRelated[i]); // Expect there is only one relation String relAttrName = null; if ((rel != null) && (rel.length > 0)) { RelationRange relRange = rel[0].getRelationRange(); // Only n:1 relations. if (relRange.max == 1) relAttrName = rel[0].getRelationName(); } selVal.attr = new AIDNameValueUnitId (); selVal.attr.unitId = new T_LONGLONG(); selVal.attr.values = new TS_Value(); selVal.attr.values.u = new TS_Union(); selVal.attr.values.u.stringVal(""); selVal.attr.attr = new AIDName(); selVal.attr.attr.aid = ae.getId(); selVal.attr.attr.aaName = AUFTRAG_ID; selVal.value = new TS_Value(); selVal.value.u = value; selVal.oper = SelOpcode.EQ; query.condSeq = new SelValue[1]; query.condSeq[0] = selVal; query.operSeq = new SelOperator[0]; query.orderBy = new SelOrder[1]; query.orderBy[0] = new SelOrder(new AIDName(ae.getId(), AUFTRAG_ID), true); query.relInst = new org.asam.ods.ElemId(); query.relInst.aid = new T_LONGLONG(); query.relInst.iid = new T_LONGLONG(); query.relName = ""; ElemResultSet[] rs = as.getSession().getApplElemAccess().getInstances(query, 0); aidNUID[index] = new AIDNameUnitId(); aidNUID[index].attr = new AIDName(); aidNUID[index].attr.aid = ae.getId(); aidNUID[index].attr.aaName = relAttrName; aidNUID[index].unitId = new T_LONGLONG(); ++index; } © 06/2005 IstecODS: Effiziente Entwicklung mit Hilfsklassen ODS Abfrage mit Hilfsklasse: QueryStructureGenerator qsg = new QueryStructureGeneratorImpl(); qsg.setAttributesToFetch(getViewApplicationElement()); // SQL: ‚select * …‘ TS_Union value = new TS_Union(); value.longVal(auftragId); qsg.addCondition(getViewAID(), AUFTRAG_ID, value, SelOpcode.EQ); // SQL: ‚where ..‘ qsg.addOrderBy(getViewAID(), AUFTRAG_ID, true); ODSResultSet rs = qsg.executeQuery(getApplElemAccess()); Vorteile: • Korrekte und vollständige Befüllung von org.asam.ods.QueryStructure Instanzen • Verständlicher und überschaubarer Quellcode • Transparente Cache Nutzung © 06/2005 IstecODS: Effiziente Entwicklung mit Hilfsklassen ODS Ergebnisauswertung ohne Hilfsklasse: ElemResultSet[] rs = as.getSession().getApplElemAccess().getInstances(query, 0); System.out.println("ResultSet size = " + rs.length); for (int i = 0; i < rs.length; ++i) { for (int k = 0; k < rs[i].attrValues.length; ++k) { NameValueSeqUnitId nvsuid = rs[i].attrValues[k].attrValues; System.out.println("ValueName (" + i + ":" + k + "): " + nvsuid.valName); switch (nvsuid.value.u.discriminator().value()) { case DataType._DT_STRING: String[] valuesString = nvsuid.value.u.stringVal(); for (int m = 0; m < valuesString.length; ++m) System.out.println("Value (" + m + "): " + valuesString[m]); break; case DataType._DT_LONG: int[] valuesInt = nvsuid.value.u.longVal(); for (int m = 0; m < valuesInt.length; ++m) System.out.println("Value (" + m + "): " + valuesInt[m]); break; case DataType._DT_BOOLEAN: int[] valuesBoolean = nvsuid.value.u.longVal(); for (int m = 0; m < valuesBoolean.length; ++m) System.out.println("Value (" + m + "): " + valuesBoolean[m]); break; /* * ... * ... */ default: break; } } } © 06/2005 IstecODS: Effiziente Entwicklung mit Hilfsklassen ODS Ergebnisauswertung mit Hilfsklasse: ODSResultSet rs = qsg.executeQuery(getApplElemAccess()); while (rs.next()) { Integer auftragid = rs.getInteger("AUFTRAG_ID"); String kommentar = rs.getString("AUFTRAG_KOMMENTAR"); Boolean dauerlauf = rs.getBoolean("AUFTRAGDAUERLAUFKZ"); /* * ... * ... */ } Vorteile: • Zeilenorientierte Verarbeitung der spaltenorientierten QueryErgebnismenge org.asam.ods.ElemResultSet[] • Automatische Typkonvertierung • Verständlicher und überschaubarer Quellcode © 06/2005 Datenkonsistenz mit Hilfe von DB Triggern Aufgabe: Datenkonsistenz gewährleisten bei konkurrierenden Datenzugriffen von mehreren ODS-Clients auf einem ODS-Server. Lösung: Via Datenbank-Trigger wird ein Attribut ‚Version‘ zur Datensatzversionierung erhöht. Dieses Attribut ist in der SVCATTR als ‚AUTOGENERATE‘ markiert. Zwischenzeitliche Änderungen am Datensatz durch einen anderen ODS-Client sind durch Prüfen der Versionsnummer auf Gleichheit feststellbar. © 06/2005 Datenkonsistenz mit Hilfe von DB Triggern Beispiel für einen Trigger: CREATE OR REPLACE TRIGGER tau_tblauftrag before update or insert on tblauftrag for each row begin if inserting then :NEW.sysanleger := RTRIM(USER); :NEW.version := '1'; :NEW.odsname := 'AUFTRAG-' || TO_CHAR(:NEW.auftragid); else :NEW.version := TO_CHAR(TO_NUMBER(:OLD.version) + 1); end if; :NEW.aendererdz := SYSDATE; :NEW.sysaenderer := RTRIM(USER); end; © 06/2005 Ausblick • Umsetzung der in ODS 5 spezifizierten ‚Extended Query Structure‘ (org.asam.ods.QueryStructureExt) sollte den Einsatz von DatenbankViews weitgehend unnötig machen. • Derzeit ist das Lesen von Blobs nur über Instanzelemente möglich. Einstellen von DT_Blobs in das org.asam.ods.ElemResultSet[] wenn angefordert erspart Roundtrips. © 06/2005 Danke für die Aufmerksamkeit. © 06/2005