[ nebojsa4 @ 09.10.2009. 13:44 ] @
Pozdrav svima.
Zna da se vec vise puta pisalo po pitanju ove teme, ali nikada nesto konkretno (ili bar ja nisam shvatio).
Evo sta me interesuje :

Pravim program u VB5 (isti djavo kao i da je VB6) i zelim da ga postavim na vise racunara (3-5 kom) ali da se samo na jednom nalazi baza podataka.

Koristim iskljucivo vb5/6 i DAO (znaci, nemojte odgovarati - ostavljati postove tipa predji na ADO, koristi nesto drugo, itd, itd, jer na kraju dodje do prepucavanja i totalnog skretanja sa teme, sto sa vec ovde i vidjao - unapred hvala na razumevanju).

Ovako:
- imam jednu bazu npr. test.mdb koja se nalazi iskljucivo na jednom comp-u, a prog. bi bio na svim racunarima.
- e sada: kako, koristeci DAO, da vrsim pristup toj bazi, jer ja sam pokusao sledece, sto cu i opistati, i na koje sam probleme naletao:

- baza "test.mdb" :
===================
Sifra dbLong
Prezime dbText,50
Ime dbText,50
Adresa dbText,50
Mesto dbText,50
Telefon dbText,50
Datum dbDate
UObradi dbBoolean

Indexes: PoSifra

otvaranje baze sam radio na sledeci nacin :

Module.bas:
-----------
Global ws as Workspace
Global db as Database
Global rs as Recordset
Global SifKor 'ako je 0 vrsi se novi upis, a ako je 1 i vise vrsi se izmena podataka
'ucivatavanje baze i recordset-a

Form1.frm:
----------
Sub ListajKorisnike()
'... upisivanje tj. ubacivanje podataka iz Recordset-a u Grid tabelu
End Sub

Sub Form1_Load()
compName = Environ("computername")
Set ws = CreateWorkspace(compName,"admin","") '-bez ono dbUseJet
Set db = ws.OpenDatabase("\\SERVER\Test\test.mdb")
Set rs = db.OpenRecordset("Podaci")

'inace, u ovoj formi se Grid (ComponentOne FlexGrid) u kojoj se nalazi spisak npr. radnika (neka vrsta evidencija radnika po objektima)
End Sub

Sub cmd_Upis()
SifKor = 0
Form1.Enabled = False
Form2.Show
End Sub

Sub smd_Izmena()
SifKor = 1
Form1.Enabled = False
Form2.Show
End Sub

Form2.frm:
----------

Sub Form2_Load()
If SifKor = 0 then
If rs.RecordCount > 0 Then
rs.Index = "PoSifra"
rs.MoveLast
SifKor = rs!Sifra + 1
Else
SifKor = 1
End If
ws.BeginTrans
rs.AddNew
rs!Sifra = SifKor
rs!UObradi = True
rs.Update
ws.CommitTrans
Else
rs.Index = "PoSifra"
rs.Seek "=", SifKor
'... Upisivanje podataka u TextBox-ove
End If
End Sub

Sub cmd_Save()
rs.Index = "PoSifra"
rs.Seek "=", SifKor
ws.BeginTrans
rs.Edit
rs!Sifra = SifKor
rs!UObradi = False
'... i upis ostalih podataka u RecordSet
rs.Update
ws.CommitTrans
Form1.Enabled = True
Unload Me
Form1.Show
Form1.ListajKorisnike
End Sub

E, o cemu se radi!
Kada vrsim upis podataka u recordset koristeci BeginTrans, AddNew i CommitTrans sve je ok - izvrsi se fizicki zapis u test.mdb
Ali, kada koristim BeginTrans, EDIT i CommitTrans - tu nastaju problemi.
Namerno sam postavio polje UObradi (True or False) da, cim neko od korisnika pokrene Izmenu podataka o selektovanom radniku, se automatski snimi kao False tako da ostali korisnici mogu vrsiti samo pregled a ne i obradu podataka o datom radniku.
I kada se vrsi novi upis radnika (mislim na novog radnika u firmi) odmah se u bazu upisuje nova sifra i UOBradi = True tako da data Sifra radnika bude automatski zuzeta... kako ne bi doslo do konflikta ako, kojim slucajem, dva korisnika pokrenu upis novih radnika - da se ne bi dodelila ista Sifra za dva razlicita radnika. Nadam se da ste me shvatili.

U cemu je problem?
Pokrenem Upis novog radnika, cim se ucita Form2 odmah se fizicki u test.mdb upisuje novi zapis (samo Sifra, a UObradi se postavlja na True),
izvrsi se upis podataka o radniku (u TextBox-ove) i klikne se na Snimi - gde se, ustvari, vrsi Seek vec rezervisanog zapisa i vrsi se rs.Edit.
Kada se odradi rs.Update i ws.CommitTrans, unload-uje se Form2, prikazuje se Form1 i pokrece se procedure ListajKorisnike... ALI, nista ne prikazuje.
Proverim test.mdb i, ono, nema nikakvog zapisa posle koriscenja rs.Edit-a ??!!???

Da li mi neko moze pomoci kako da resim ovaj problem?
Sve dok koristim AddNew radi ok, ali cim editujem postojeci zapis u recordset-u, ne samo da ne izvrsi editovanje/izmene nego i izbrise mi taj zapis koji je vec postojao sa delimicno popunjenim poljima.

Sve se svodi na to, kada neko edituje odredjeni zapis, ostali mogu sam da vrse pregled i da, cim se izvrsi promena podataka u recordset-u to iskaze i ostalima (jer, ako dobro shvatam, svaki korisnik koji pokrece program stvara se WorkSpace).

Unapred hvala svima mnogo.

P.S. Kao sto rekoh na pocetku poruke, iskljucivo VB5/6 i DAO. Znam da neki koriste (mozda i vecina) ADO itd.. ali ja stvarno nemam vremena da sve prepravljam da bih prilagodio ADO-u i jos da mozgam oko toga. Koristim DAO od prvog dana kako sam poceo sa prog. u vb-u i, verujte mi, da mije tesko da se sada, trenutno, prilagodjavam necemu drugome (mada sam malo gledao po forumima i rad sa ADO i jednostvano mi neke stvari ne idu u glavu).
A misljenja tipa, predji na MySQL i tako nesto... zamolio bih vas da to ostavite za neku drugu temu, nego da se bavimo iskljucivo ovom temom.

Jos jednom, unapred hvala svima.

Pozdrav.
[ captPicard @ 09.10.2009. 14:45 ] @
Odgovoriti ću ti samo na dio pitanja.

1. Nije pametno koristiti polje u bazi da vidiš da li je record u stanju editiranja. Zašto? Evo: šta ako radnik krene editirati record i ode na marendu? I, šta ako pukne baza u trenutku editiranja? Kako ćeš ponovo otključati record?

2. Da bi ti odgovorio ovo oko rs.edit morao bi dati malo vise koda. napiši šta točno radiš u sub listajkorisnike
[ nebojsa4 @ 09.10.2009. 16:17 ] @
Na zalost, pod onim 1, si u pravu :((( (kako coveku ove sitne i jednostavne stvari izmaknu, ccc - tu cu morati uraditi nesto drugo)

Pod 2: Vrsi listanje podataka, evo kod (inace koristim ComponentOne FlexGrid) :

Sub ListajKorisnike()
With Grid1
.Rows = 1 'inace Row = 1 je FixedRow i to je, ustvari, zaglavlje Grid-a
rs.Index = "PoPrezimeIme"
rs.MoveFirst
Do
.Rows = .Rows + 1
.Row = .Rows - 1
.Col = 0: .Text = rs!Sifra
.Col = 1: .Text = rs!Prezime
.Col = 2: .Text = rs!Ime
.Col = 3: .Text = rs!Adresa
.Col = 4: .Text = rs!Mesto
rs.MoveNext
Loop Until rs.EOF
.Row = 1: .Col = 0
End With
End Sub

Ali, problem je u tome, kada vrsim editovanje (rs.Seek, zatim ws.BeginTrans, pa rs.Edit, onda rs.Update i na kraju ws.CommitTrans) ne samo da se ne izvrse izmene, fizicki, u bazi nego isti taj zapis bude obrisan (fizicki iz baze ??). AddNew radi bez problema - zapis ostaje fizicki snimljen u bazi i redovno se prikazuju pokretanjem procedure ListajKorisnike

Pozdrav.
btw. Thx na pomoci & unapred hvala na daljoj pomoci.

[Ovu poruku je menjao nebojsa4 dana 09.10.2009. u 17:27 GMT+1]

[Ovu poruku je menjao nebojsa4 dana 09.10.2009. u 17:28 GMT+1]

[Ovu poruku je menjao nebojsa4 dana 09.10.2009. u 17:29 GMT+1]
[ captPicard @ 09.10.2009. 19:50 ] @
postoji rješenje i za ovaj način kako si ti napravio, ali nije baš pametno, pa ti ga niti neću reći :-)

Bilo je davno kada sam radio u VB, i ovako ne vidikm gdje bi mogla biti greška. Ako želiš, pošalji mi projekt na mail, pa mogu pogledati.

galileoMAJMUNpu.t-com.hr
[ Marko_L @ 09.10.2009. 20:00 ] @
Kada se radi o višekorisničkom okruženju, vrlo je bitno da se ne dozvoli da jedan korisnik može da ometa rad drugih, bilo tako što će zaključati bazu, pa zaboraviti da je otključa bilo tako što će non stop nešto čačkati po podacima pa će ostali morati da čekaju na red. Tako da, direktan pristup bazi otpada, već mora da postoji neki layer između baze i klijenata, tj. klijent aplikacija. E sad, ako nećeš da koristiš ADO i SQL Server koji sam po sebi predstavlja taj layer, moraćeš da napraviš svoj. Naravno, to ne mora da bude neka komplikovana server aplikacija, već može da bude mini server koji može da komunicira sa klijentima i sa bazom, tako što će od klijenata primati određene zahteve (dodaj podatke, izmeni podatke, izvrši taj i taj query i pošalji mi rezultat... itd.). Taj server bi imao svojevrsni queue, tako da kada dva klijenta zatraže isti podatak, neće doći do konflikta. Komunikaciju između klijenta i servera možeš ostvariti pomoću Winsocka recimo.

Evo kako bi to trebalo da izgleda. Klijent pošalje serveru svoj ID, svoju IP adresu u mreži i zahtev sa parametrima (koja akcija, koja tabela, koja polja). Server obrađuje zahtev, proverava queue i ako je prazan izvršava ga odmah, a ako nije stavi na na kraj liste i obrađuje po završetku svih ostalih (u praksi će queue u 99% slučajeva biti prazan, ali treba da postoji za slučaj da dva klijenta pošalju zahtev u istoj sekundi ili je baza toliko narasla da treba par sekundi da se obradi zahtev). Kada izvrši zahtev, zavisno od toga kakav je on bio, server šalje povratnu informaciju njemu ili svim klijentima koji su u mreži... tj. ako je neki podatak dodat, promenjen ili izbrisan, onda se šalje update svim klijentima... a ako je zatraženo da se izvrši query sa ciljem da se dobiju filtrirani podaci, onda se vraća samo klijentu koji je to zatražio. Takođe, ta server aplikacija bi trebalo da ima i logovanje svih pristupa... tako da imaš jednu tabelu koja sadrži podatke o tome koji klijent, pod kojim korisničkim imenom, u koje vreme je izvršio neku operaciju i koju i nad kojim podatkom. Ovo je vrlo bitno, jer ako se pojave neke nelogičnosti u bazi, može da se trejsuje kada i kako je ta nelogičnost nastala i ko je doveo do toga, a ako toga nema, svaki nezadovoljni radnik bi mogao da sabotira bazu i niko ne bi znao da je to on uradio. Naravno, svaki zahtev bi morao da ima START i END string, tako da ako nema ta dva, onda server označi zahtev kao neispravan i ne vrši nikakvu operaciju nad bazom, već vraća klijentu poruku da ponovi zahtev. Ovo recimo rešava problem kada u toku slanja pukne mreža ili klijentov računar, pa ne stignu svi paketi na odredište, itd. Sve su to problemi na koje treba misliti.

E sad, ovo možda zvuči komplikovano, ali ako si već krenuo da praviš multikorisničko okruženje, ne možeš očekivati da sve bude isto kao kad radiš bazu za jednog korisnika. Jednostavno, višekorisničko okruženje zahteva mnogo veću kontrolu pristupa i rešavanje gomile problema od kojih je Picard neke već pomenuo (korisnik pristupi bazi i ode na kafu ili još gore završi smenu, a ostali ne mogu ništa s njom da rade). Isto će se desiti ako nešto pukne prilikom rada sa podacima od strane nekog klijenta, pa baza ostane zaključana (pritom ne mislim samo na zaključavanje tvojom true false metodom, već i accessovima lockovanjem) ili ne daj Bože pukne mreža. Pored toga, tu su i problemi sa logovanjem, sa pucanjem baze i slično. Jednostavno, mora da postoji layer koji će to da razrešava i sačuva podatke u slučaju kvara, jer nikad nije dobro da radnik može namerno ili slučajno da napravi havariju i stopira celu firmu zato što nije uneo podatak kako treba, a da o tome ne postoji log koji će uperiti na tog radnika.

U svakom slučaju, znam d si rekao da ne predlažemo ništa drugo, ali kako god okreneš, siguran sam da bi bilo lakše naučiti ADO i preći na SQL Server, jednostavno manje posla. Istina je da, iako nema nekih velikih razlika između ADO i DAO, ADO može u nekim slučajevima da zbuni nekoga ko dugo radi sa DAO (ja i dan danas koristim DAO kada radim jednokorisničke aplikacije, jer jednostavno brže radim u njemu), ali opet to je ništa u odnosu na ono što te čeka ako hoćeš da napraviš stabilnu multikorisničku aplikaciju koristeći DAO. Naravno, izbor je tvoj i ako si baš tako rešio, onda radi... ovde ćeš svakako dobiti svaku pomoć koja ti treba. Ali spremi se da ćeš morati da se pozabaviš ne samo radu sa bazom, već i mrežnim protokolima i komunikacijom.
[ captPicard @ 09.10.2009. 20:11 ] @
E mislim da si ga sada stvarno uplašio

Ali je na njegovu žalost sve šta si rekao istina...
[ nebojsa4 @ 10.10.2009. 09:17 ] @
Lako je vama se zezati :-)
Salim se.

Narode, evo pokusavam malo da skontam ADO - pa sam nesto i pokusao.
Naravno, sve ce ovo delovati pocetnicki ali jbg

Jedno pitanje (malo sam gledao i na drugim forumima o ADO, itd) :

Prilikom svake izmene, dodoavanja novog ili brisanja zapisa (naravno, uvek govorim o fizickom brisanju iz baze) se mora uspostaviti konekcija i nakon izvrsenja prethodno navedenih operacija ista se zatvara da bi fizicki imala efekta na bazu?

Evo primer kako sam uradio i funkcionisalo je:
'imam jedan Grid, TextBox i CommandButton

Dim aConn As ADODB.Connection
Dim rs As ADODB.Recordset
Const sqlConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\mp3Kat.mdb;Mode=ReadWrite;Persist Security Info=False"

Sub Listaj()
Set aConn = New ADODB.Connection
aConn.Open sqlConn
Set rs = New ADODB.Recordset
rs.Open "SELECT * FROM Pesme WHERE Sifra=2", aConn 'Sifra je sifra Grupe ciji se spisak po Nazivima istih nalazi u drugom Recordset-u
rs.MoveFirst
Grid1.Clear
Grid1.AllowUserResizing = flexResizeColumns
Grid1.Rows = 1
Do
Grid1.Rows = Grid1.Rows + 1
Grid1.Row = Grid1.Rows - 1
Grid1.Col = 0: Grid1.Text = rs!RedBr
Grid1.Col = 1: Grid1.Text = rs!Naziv
Grid1.Col = 2: Grid1.Text = rs!Velicina
rs.MoveNext
Loop Until rs.EOF
rs.Close
aConn.Close
Set rs = Nothing
Set aConn = Nothing
End Sub

Private Sub Command1_Click()
Set aConn = New ADODB.Connection
Set rs = New ADODB.Recordset
aConn.Open sqlConn
rb = CLng(Grid1.Cell(flexcpText, Grid1.Row, 0))
rs.Open "SELECT * FROM Pesme WHERE RedBr=" + Trim(Str(rb)), aConn, adOpenKeyset, adLockOptimistic
rs.Delete adAffectCurrent
Set rs = Nothing
aConn.Close
Set aConn = Nothing
Listaj
End Sub

Private Sub Form_Load()
Listaj
End Sub

Private Sub Grid1_Click()
Text1.Text = Grid1.Cell(flexcpText, Grid1.Row, 1)
End Sub

Private Sub Text1_GotFocus()
Text1.SelStart = 0
Text1.SelLength = Len(Text1)
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii = 13 Then
Set aConn = New ADODB.Connection
aConn.Open sqlConn
Set rs = New ADODB.Recordset
KeyAscii = 0
rb = CLng(Grid1.Cell(flexcpText, Grid1.Row, 0))
MsgBox rb
rs.Open "SELECT * FROM Pesme WHERE RedBr=" + Trim(Str(rb)), aConn, adOpenKeyset, adLockOptimistic
rs!Naziv = UCase(Text1)
rs.UpdateBatch adAffectCurrent
Set rs = Nothing
aConn.Close
Set aConn = Nothing
Listaj
End If
End Sub


Jer valja ovo ovako? mislim, ima efekta (sigurno moze i bolje)


btw. hvala svima koji se javljaju - puno mi znaci ova pomoc.
btw2. samo me sada nemojte cimati sto pokusavam da koristim ADO object (malo sam tezak po pitanju prelaska i ucenja necega novog jer mi vreme bas i ne dozvoljava).
[ djpero.84 @ 13.11.2009. 12:49 ] @
@nebojsa4

Ja sam isto radio DAO na MSaccess bazi kad se radilo o jednokorisnickoj bazi, a sad radim na ADO MS SQL i nije uopste neka migracija
kako sam ja mislio i prepao se gdje cu sad sve to mjenjati i sl, ali je svakako bolje.

Evo ti par mojih prijedloga ako hoces:

Napravi modul sa procedurama za pozivanje query-ja i svim ostalim parametrima koji su uslov za konekciju prema serveru:

- Ono je najbolje raditi sa ConnectionStringom i to ka DNSname servera ako ti vec nije fixna IP adresa
- nadam se da razumijes da su ove "db_name....." moras zamjeniti tvojim podacima

Code:
Option Explicit
Public conn As ADODB.Connection
Public rs As ADODB.Recordset
Public db_name As String, db_server As String, db_port As String, db_user As String, _
 db_pass As String, constr As String, db_trusted_conn As Integer

Public Function OpenConn()

    constr = "Driver={SQL Native Client};Server=" & db_server & ";Database=" & db_name & ";UID=" & db_user & ";PWD=" & db_pass

    If db_trusted_conn = 1 Then
       constr = constr & ";Trusted_Connection=yes;"
    End If
    
    Set conn = New ADODB.Connection
    conn.ConnectionTimeout = 5
    On Error GoTo err2
    If conn.State = 1 Then
        conn.Close
    End If
    
    conn.Open constr
    OpenConn = 1
    Exit Function
err2:
    OpenConn = conn.State
End Function



- a pozivanje Query-ja mozes ovako da napravis cisto da malo smanjis kod:
Code:
Public Function NewQuery(SQLquery As String)
    Set rs = New ADODB.Recordset
    rs.CursorLocation = adUseClient
    rs.CursorType = adOpenStatic
    rs.LockType = adLockPessimistic
    rs.Open SQLquery, conn, adOpenStatic, adLockOptimistic, adCmdText
End Function

Public Function NewQueryClose()
    rs.Close
    Set rs = Nothing
End Function



- tek sad vidjeh da pri svakom listanju pokreces novu konekciju ka serveru sto nije dobro, stoga mozes iskoristiti ovo sto sam ti napisao gore

Pozzzz....