Tutorial VC++ 6.0 und Oracle



  • hustbaer schrieb:

    Wenn es nen OLEDB Treiber gibt, dann müsste man ADO verwenden können.

    Jo, der ist afaik auch ziemlich nett. Ich glaube es gab Probleme mit BLOB Feldern, aber vielleicht ist das mittlerweile auch behoben (War zu 8.1.7er Zeiten, als ich zuletzt damit rumgespielt hab)

    LordTerra schrieb:

    also ich hab den oracle instantclient installiert und da gibt es nen odbc treiber zum installieren ... mit dem klappt auch der zugriff so im groben ... nur irgendwie kann ich nen recordset nicht per dynaset öffnen da bekomm ich imemr ne fehlermeldung ... Ora-01460 fehler bekomm ich da immer...

    kann mir ma wer den unterschied zwischen dynaset und snapshot erklären ?
    weil ich öffne meine select anfragen alle per snapshot ...
    das geht ...

    ODBC ist mit den MFC ziemlich hässlich, sobald du mehr machen willst als nur eine Tabelle Top-Down auslesen. Ich würde mir die Kopfschmerzen ersparen und ADO verwenden.



  • Cpp_Junky schrieb:

    ODBC ist mit den MFC ziemlich hässlich, sobald du mehr machen willst als nur eine Tabelle Top-Down auslesen. Ich würde mir die Kopfschmerzen ersparen und ADO verwenden.

    hmmm und wo bekomm ich dafür die treiber ?
    bzw reicht es da treiber zu benutzen ?

    also so richtig hab ich das noch nicht verstanden ...
    also so wie ich das seh:
    ich installiere nen db-treiber (z.b. den von oracle direkt)
    dann geh ich in mein vc++6.0 und binde dort die afxdb.h ein und kann dann per cdatabase / crecordset auf die db zugreifen ...

    wie ist das nun wenn ich ado benutze ?
    muss ich dann nur den treiber benutzen ?
    oder brauch ich dann auch ne andere vc++6.0 header datei ?
    oder kann ich dann die selben befehle benutzen ?



  • Für ADO Zugriff gibt es keine MFC Klasse. Stattdessen kannst du einfach die DLL importieren. Dann stehen dir für alle ADO Klassen Interfaces zur Verfügung. Das klingt komplizierter als es ist. Hier ein gutes Tutorial mit dem ich mich damals auch da reingearbeitet habe:
    http://www.codeproject.com/KB/database/simpleado.aspx



  • hmmm ok... verrat mir mal wo da der unteschied is ... also für meine zwecke denke ich das odbc reicht ich will nur hin und wieder daten selectieren und inserts und updates ausführen und das geht eigentlich recht gut ...

    also wieso ist ado besser?



  • ADO ist eine eigene Architektur in Form von ActiveX Komponenten. ADO ist letztlich afaik nichts weiteres als eine komfortable Kapselung von OLE DB. Daher werden auch primär OLEDB Provider analog zu ODBC Treibern verwendet.

    Die Diskussion ADO vs ODBC (Ich beziehe mich hier stets auf die MFC Klassen) ist etwas kontrovers ^^ Daher kann ich nur meine eigenen Erfahrungen schildern: Unter ODBC hatte ich z.B massive Probleme mit der Manipulation von Isolations-Leveln (Transaktions-Isolations Level), Deadlock-Handling und am allerschlimmsten: Implementierung von ordentlichen Recordsets. Das Recordset Objekt will z.B im Voraus wissen, wie viele Felder zurückkommen. Lustig oder? Vor allem, wenn man select *, oder joins ausführt. Es gibt natürlich einen Trick, indem man das Recordset mit der maximalen Feldzahl initialisiert und NACH dem Ausführen des Statements zurechtbiegt. Dann hab ich mich mit dem Recordset Typ rumgeschlagen. Ich brauchte ein Recordset mit Bi-Direktionalem Scrolling und der Isolations-Eigenschaft READ_COMMITTED. Krampf! Wenn man sich die ODBC Doku durchliest stösst man immer wieder auf Stellen a la "Eigenschaft A nur in Verbindung mit Eigenschaft B und nicht wenn Eigenschaft C zutrifft". Wirklich grauenvoll. Unter ADO übergebe ich die standardisierte Parameter und es funzt. Der Witz an der Sache ist der, das es sogar funktioniert wenn ich dabei den schrottigen ODBC Treiber verwende (ADO via ODBC) 😃 Für einfache Sachen ist ODBC natürlich sehr komfortabel, keine Frage.



  • moin

    puh da sind wörter in deiner erklärung die ich noch nie gehört hab *ggg*
    ok was ich bei deiner erklärung nicht versteh ich folgendes:
    du schreibst man müsst z.b. wenn man select * ausfrüht dem recordset sagen wieviel da zurück kommt ...
    das konnte ich bisher nicht fest stellen... wenn ich nen select * mache benutze ich danach ne while schleife die ich auf eof abfrage und durch die einzelnen datensätze navegiere ich mit rs.MoveNext();

    oder beziehst du dich in diesem fall auf die spalten der tabelle ???
    das wäre eh ne frage die ich einem meiner nächsten threads gestellt hätte: wie bekomme ich raus wieviele spalten eine tabelle hat ...
    <--- funktioniert das unter odbc nicht ? bzw geht das nur mit ado ?

    und dann hab ich schon öffters diese geschichte mit dem scrolling gelesen ... was meint ihr damit? wenn ich nen select mache kann ich doch moveNext bzw moveLast usw benutzen um den vorher gelesenen datensatz zu bekommen ...

    das ist doch das scrolling was ihr meint oder???

    so erstma genug fragen ... 🙂
    mfg LT



  • Das Problem mit dem Recordset bestand darin, das ich eine einzige Klasse implementieren wollte, mit der man alle Tabellen abfragen kann (nicht wie beim Class Wizard, der einem EINE Recordset-Klasse pro Tabelle erstellt). Des weiteren muss ein Data-Binding erfolgen, wenn über das Recordset änderungen geschrieben werden sollen. Dies erfordert, die Tabellenstruktur im Voraus zu kennen (Oben schonmal kurz angesprochen). Wie gesagt, das lässt sich alles irgendwie handhaben, aber es ist sehr umständlich und fehleranfällig.
    Was die Navigation angeht, kommt es drauf an was du machen willst: Ich hatte damals ein GUI mit dem man frei durch das Abfrage-Ergebnis scrollen und die Daten gleichzeitig bearbeiten können sollte. Dies erfordert ein Recordset, das sich vor UND zurück bewegen lässt - Was nicht selbstverständlich ist. Es erfordert die Unterstützung durch die DB, durch den Treiber und einen speziellen Typ Recordset. Und zu allem Übel war die Umgebung noch Multi-User fähig, so das ich konkurrierende Zugriffe behandeln musste. Und da fing der Spass dann auch richtig an 🙂



  • @LordTerra:
    Ich würde dir ADO empfehlen, nicht weil ADO irgendwie "mächtiger" wäre, sondern weil ADO viel einfacher ist, vor allem für Einsteiger. Plus du findest ca. 100x mehr Beispiele zum Thema ADO als zu ODBC.

    Mittlerweile, also seit ADO .NET so gross geworden ist, findet man immer weniger Beispiele für ADO (ohne .NET), aber viel ist gleich oder fast gleich geblieben. Und auf C++ Seiten findet man immernoch ADO (ohne .NET) Code, da die wenigsten C++/CLI verwenden wollen, nur um mit ADO .NET arbeiten zu können.



  • so ok sagen wir ma ch will das ado ausprobieren ...

    dann hat mir der cpp_junky ja schon nen link geschickt wo nen beispiel drin ist ...
    aber da ist ne dll verlinkt die bei dem beispiel mal wieder net bei ist ... (msado15.dll)
    wo bekomm ich diese dll ? und ma doof gefragt : wie binde ich die ein ? das letzte mal als ich das versucht hab hat das nämlich net geklappt und wenn ihr wisst wie das geht wäre ne kurze anleitung echt nett 🙂



  • ok ich denke das hab ich selbst hin bekommen ...
    aber wie bau ich nun mit dem ado ne verbindung zu ner oracle db auf ???

    also das was mir grad kopfzerbrechen bereitet:

    bei der oracle variante per odbc hab ich ja nen odbc treiber installiert und dann per hand nen objekt unter: start -> einstellung- > systemsteuerung -> verwaltung -> datenquellen (odbc) erstellt (ArgusDB) und auf dieses objekt hab ich dann noch nen dsn erzeugt und daraufbezieh ich mich ja in meinem connect über odbc ... nur wie geht das unter ado bzw muss ich mich da auf die selbe datei beziehen ? also da bin ich grad am grübeln...



  • Für ADO musst du dir nur die entsprechende Syntax für den Connection-String suchen. Sollte im Netz recht einfach zu finden sein.
    DSN brauchst du keinen. (Ich mag die ODBC DSN Einträge auch nicht, finde das persönlich doof wenn ein Programm erfordert dass ich in meinem System irgendwas erstelle/einstelle damit es geht)



  • hmmm ok dann guck ich da mal ob ich was finde ...
    für den moment hab ich halt den dsn eintrag genutzt ...
    der geht ja auch unter ado ...

    ich hab da aber nen problem mit dem updaten von daten ...
    und zwar ist in dem code beispiel von codeproject zwar ne update funktion drin aber irgendwie funktioniert die bei mir nicht ...

    also folgendes: ich geh in meiner updatefunktion so vor: ich selectiere mir die entsprechende zeile die ich updaten will -> überschreibe das feld mit dem neuen wert -> und sage pRecordset->UpdateBatch(adAffectAll); -> und schliesse das recordset

    ^^ das ist laut testprogramm eigentlich alles was ich machen muss
    nur irgendwie funktioniert das nicht ...
    also wenn ich mir
    pRecordset->Fields->GetItem((_variant_t)feld)->Value und (_bstr_t) wert;
    ausgeben lasse bekomm ich auch genau das was ich haben will
    wenn ich jetzt aber sage:
    pRecordset->Fields->GetItem((_variant_t)feld)->Value = (_bstr_t) wert;
    und
    pRecordset->UpdateBatch(adAffectAll);
    passiert gar nix ...

    wieso ist das so ? bzw was mach ich da falsch ?



  • ah fehler gefunden ...

    pRecordset->Open ((IDispatch 😉 pCommand, vtMissing, adOpenStatic,
    adLockBatchOptimistic, adCmdUnknown);

    ^^ hatte ich vergessen ... hatte das im readonly modus geöffnet



  • ok soweit sogut ...
    eine frage hätte ich dann noch ... in dem ado test beispiel ist leider keine variante beschrieben wie man nun auch datensätze aus so nem record löschen kann ... habt ihr da noch nen beispiel parat oder kann mir wer sagen wie das geht ?



  • moin ich nochmal

    also ich hab derzeit arge probleme datensätze zu inserten ...
    das geht irgendwie nicht ...
    also ich hab derzeit nicht so richtig nen plan wie ich per ado nen insert oder nen delete machen kann ... brauche dazu bitte ne erklärung oder ne website ...



  • Entweder als SQL Statement per Execute Funktion über das Connection Objekt oder per Recordset Objekt mit AddNew bzw Delete



  • ich hab mich mal an nem insert versucht ...
    fehlermeldunge konnen net aber er führt auch nix aus ...

    aufruf:
    if(DBConnectNow()){
        DBInsertNow("INSERT INTO test (id, name) VALUES (55, 'testinsert');");
        DBCloseNow();
    }
    
    DBConnectNow():
    BOOL CACS400App::DBConnectNow(){
    	CoInitialize (NULL);
    
    	// Stablishing a connection to the datasource
    	try	{
    		HRESULT hr = m_pConn.CreateInstance (__uuidof (Connection));
    
    		if (FAILED (hr)){
    			AfxMessageBox ("Can't create intance of Connection");
    			return FALSE;
    		}
    
    		if (FAILED (m_pConn->Open (_bstr_t ("Provider=MSDAORA.1;Password=pw;User ID=system;Data Source=PROD.WORLD;Persist Security Info=True"),
    					_bstr_t (""), _bstr_t (""), adModeUnknown))){
    			AfxMessageBox ("Can't open datasource");
    			return FALSE;
    		}
    	}catch ( _com_error &e ){
    		_bstr_t bstrSource (e.Source());
    		_bstr_t bstrDescription (e.Description());
    		TRACE ( "Exception thrown for classes generated by #import" );
    		TRACE ( "\tCode = %08lx\n", e.Error ());
    		TRACE ( "\tCode meaning = %s\n", e.ErrorMessage ());
    		TRACE ( "\tSource = %s\n", (LPCTSTR) bstrSource);
    		TRACE ( "\tDescription = %s\n", (LPCTSTR) bstrDescription);
    
    		AfxMessageBox ((LPCTSTR) bstrDescription);
    		return FALSE;
    	}catch (...){
    		TRACE ( "*** Unhandled Exception ***" );
    		return FALSE;
    	}
    
    	return TRUE;		
    }
    
    DBInsertNow():
    BOOL CACS400App::DBInsertNow(CString aktion){
    	try	{
    		_CommandPtr pCommand;
    
    		HRESULT hr = pCommand.CreateInstance (__uuidof (Command));
    
    		if (FAILED (hr)){
    			AfxMessageBox ("Can't create an instance of Command");
    			return FALSE;
    		}
    		// Pointer to ADO data connection //
    		m_pConn->Execute((_bstr_t) aktion, NULL, adExecuteNoRecords);
    	} 
    	catch( _com_error &e )	{
    		_bstr_t bstrSource(e.Source());
    		_bstr_t bstrDescription(e.Description());
    		TRACE( "Exception thrown for classes generated by #import" );
    		TRACE( "\tCode = %08lx\n", e.Error());
    		TRACE( "\tCode meaning = %s\n", e.ErrorMessage());
    		TRACE( "\tSource = %s\n", (LPCTSTR) bstrSource);
    		TRACE( "\tDescription = %s\n", (LPCTSTR) bstrDescription);
    		return FALSE;		
    	}catch (...){
    		TRACE ( "*** Unhandled Exception ***" );
    		return FALSE;		
    	}
    	return TRUE;		
    }
    
    DBCloseNow():
    void CACS400App::DBCloseNow(){
    	if ( (m_pConn->State & adStateOpen) == adStateOpen)
    		m_pConn->Close();
    
    	CoUninitialize();
    }
    

    *edit*: ok es scheint so als ob der insert eigentlich geht, aber irgendwie bekommt mein programm von der db die fehlermeldung: ORA-00911: invalid character
    nur frag ich mich wieso das so ist ... hat jemand ne idee ?

    mfg LT



  • - Du könntest mal mit dem Debugger durchgehen.
    - Du könntest den Netzwerkverkehr zum Datenbankserver mit Wireshark mitschneiden und schauen, ob der das Statement überhaupt absetzt.



  • wie ich geschrieben hab: der setzt den ab aber ich bekomm ne fehlermeldung: ora-00911... und das versteh ich nicht ...
    ich hab in meiner tabelle genau 2 felder:
    feld id mit integer als typ
    feld name mit vchar2 als typ

    wieso bekomm ich da invalid character zurück wenn ich 55 und 'testinsert' reinschreiben will?



  • und gleich noch ne frage:

    mir ist aufgefallen das wenn ich folgenden connectionstring benutze meine updatefunktion nicht funktioniert ...
    wieso ist das so ???

    Mit diesem open funktioniert die Update Funktion:

    if (FAILED (m_pConn->Open (_bstr_t ("Provider=MSDASQL.1;Persist Security Info=False;User ID=system;Data Source=ArgusDB"),
    

    Mit diesem open funktioniert die Update Funktion nicht:

    if (FAILED (m_pConn->Open (_bstr_t ("Provider=MSDAORA.1;Persist Security Info=False;User ID=system;Password=pw;Data Source=PROD.WORLD"),
    

    DBUpdateNow():

    BOOL CACS400App::DBUpdateNow(CString tabelle, CString feld, CString wert, CString bedingung){
    	_RecordsetPtr pRecordset;
    	try	{
    		_CommandPtr pCommand;
    		HRESULT hr = pCommand.CreateInstance (__uuidof (Command));
    
    		if (FAILED (hr)){
    			AfxMessageBox ("Can't create an instance of Command");
    			return FALSE;		
    		}
    		pCommand->ActiveConnection = m_pConn;
    		CString strQuery="select * from "+tabelle+" where "+bedingung;
    		pCommand->CommandText = (_bstr_t)strQuery;
    
    		hr = pRecordset.CreateInstance (__uuidof (Recordset));
    
    		if (FAILED (hr)){
    			AfxMessageBox ("Can't create an instance of Recordset");
    			return FALSE;		
    		}
    		pRecordset->CursorLocation = adUseClient;
    		pRecordset->Open((IDispatch *) pCommand, vtMissing, adOpenStatic, adLockBatchOptimistic, adCmdUnknown);
    
    		while (!pRecordset->GetadoEOF ()){
    			pRecordset->Fields->GetItem((_variant_t)feld)->Value = (_bstr_t) wert;
    			pRecordset->UpdateBatch(adAffectAll);
    			pRecordset->MoveNext();
    		}
    
    		pRecordset->Close ();
    	} 
    	catch( _com_error &e )	{
    		_bstr_t bstrSource(e.Source());
    		_bstr_t bstrDescription(e.Description());
    		TRACE( "Exception thrown for classes generated by #import" );
    		TRACE( "\tCode = %08lx\n", e.Error());
    		TRACE( "\tCode meaning = %s\n", e.ErrorMessage());
    		TRACE( "\tSource = %s\n", (LPCTSTR) bstrSource);
    		TRACE( "\tDescription = %s\n", (LPCTSTR) bstrDescription);
    
    		AfxMessageBox ((LPCTSTR) bstrDescription);
    
    		return FALSE;		
    	}catch (...){
    		TRACE ( "*** Unhandled Exception ***" );
    		return FALSE;		
    	}
    	return TRUE;		
    }
    

Anmelden zum Antworten