This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information

Erfassung von Eigenschaften verhindern

Allgemeine Fragen zur Entwicklung von und mit JVx.

Erfassung von Eigenschaften verhindern

Postby johnit » Fri Nov 16, 2012 6:30 pm

Kann die Erfassung von Eigenschaften verhindert werden, solange die Voraussetzungen dafür noch nicht gegeben sind?

Ein Wartungsdialog vom Master-Satz enthält Karteiblätter mit den Master-Detail (N:M) Beziehungen. Wird ein neuer Master-Satz angelegt, müssen zuerst die Pflichtfelder des Master Satzes erfasst werden. Bei der Erfassung der Master-Detail Beziehungen wird anscheinend der Master Satz zuvor gespeichert. Sind die Voraussetzungen dafür noch nicht gegeben, kommt es zu einer DB Exception.

Aus diesem Grund würde ich den Benutzer gerne zu einer "Erfassungsreihenfolge" zwingen - d.h. Teile, die noch nicht erfasst werden können, sollten deaktiviert oder unsichtbar sein. Versuche mit eventValuesChanged Listener haben nichts gebracht.

Das Problem lässt sich in der Showcase-Anwendung wie folgt nachvollziehen:

  1. neues Land (Master) anlegen - KEINEN Namen vergeben
  2. sofort Detail zum Master (Bundesland) anlegen
    => ORA-01400: cannot insert NULL into ("DEMO_SHOWCASE"."COUNTRIES"."COUNTRY") ORA-06512: at line 1
johnit
 
Posts: 45
Joined: Fri Nov 16, 2012 5:58 pm

Re: Erfassung von Eigenschaften verhindern

Postby Support@SIB » Fri Nov 16, 2012 7:05 pm

Kurze Info, warum dieses Verhalten auftritt:

Im Standard Schreibmodus „DataRow Level“ kann immer nur eine Zeile zu einem Zeitpunkt editiert werden. Die zuvor editierte Zeile wird automatisch gespeichert, bzw. wenn dies nicht möglich ist, muss sie korrigiert werden, dass sie gespeichert werden kann, bevor die nächste Zeile editiert werden darf. Dies garantiert, dass, bis auf die aktuell editierte Zeile, alle Zeilen korrekt in der Datenbank gespeichert sind.

Antwort: Ja, wie folgt

Code: Select all
private void initializeModel() throws Throwable
{
  …
  rdbMaster.eventAfterInserting().addListener(this, "doPreventEditDetails");
  rdbMaster.eventAfterDeleting().addListener(this, " doPreventEditDetails ");
  rdbMaster.eventAfterInserted().addListener(this, " doPreventEditDetails ");
  …
}


Methode 1: Bei allen Details Eingabe verhindern

Code: Select all
public void doPreventEditDetails(DataBookEvent pEvent) throws Throwable
{
  rdbDetail.setReadOnly(rdbMaster.isInserting());
}


Methode 2: Tabset disablen

Code: Select all
public void doPreventEditDetails(DataBookEvent pEvent) throws Throwable
{
  tabsetPanel1.setEnabledAt(1, !rdbMaster.isInserting());
}
User avatar
Support@SIB
 
Posts: 349
Joined: Mon Sep 28, 2009 1:56 pm

Re: Erfassung von Eigenschaften verhindern

Postby johnit » Fri Nov 16, 2012 7:26 pm

Mit doPreventEditDetails bei eventAfterInserting, eventAfterDeleting und eventAfterInserted konnte ich verhindern, dass bei einem neuen Satz sofort die Details erfasst werden.

Eigentlich möchte ich aber alle Eingaben sperren, solange der Master-Satz nicht valide ist d.h. solange die Voraussetzungen fürs speichern nicht erfüllt sind. Das gilt auch, wenn ein bestehender Satz verändert wird.
Gibt es einen Event der ausgelöst wird, wenn der Master gespeichert werden muss (wenn sich der Master verändert hat)?

Beim Default-Verhalten (WriteBackIsolationLevel DATA_ROW) werden die Sätze sofort (ohne Rückfrage) gespeichert. Das geschieht immer dann, wenn der Satz verändert wurde und dieser verlassen wird – z.B. bei Erfassung einer Detailbeziehung oder bei Auswahl eines neuen Master-Satzes aus der Liste. Dieses Verhalten führt meiner Meinung nach leicht dazu, dass der Benutzer unbeabsichtigt Änderungen speichert.

Daher habe ich versucht, ein explizites Speichern und Zurücksetzen in den Dialog einzufügen. In zwei Buttons rufe ich getDataSource().saveAllDataBooks() bzw. getDataSource().restoreAllDataBooks() auf. Das scheint aber nur mit WriteBackIsolationLevel = DATASOURCE etwas zu bringen. Ich habe daher diese Eigenschaft für den Master und die Detailbeziehungen auf DATASOURCE umgesetzt. Mehrere Änderungen können nun mit Speichern persistiert oder mit Zurücksetzen wieder zurückgenommen werden.

Dazu folgende Fragen: Geschehen diese Änderungen in einer Transaktion oder werden die erforderlichen Änderungen einfach sequentiell abgearbeitet? Können eigene Transaktionen definiert werden?

Ein weiteres Problem habe ich beim Löschen des Master Records wenn noch Detailbeziehungen erfasst sind. Da kein cascading delete definiert ist, scheitert das Löschen mit einem foreign key constraint.

Werden die Details zum Master vorher explizit gelöscht (durchgestrichen, weil WriteBackIsolationLevel = DATASOURCE), kommt es trotzdem zu einem foreign key constraint. SaveAllDataBooks versucht anscheinend zuerst den Master zu löschen. Kann die Reihenfolge beeinflusst werden?
johnit
 
Posts: 45
Joined: Fri Nov 16, 2012 5:58 pm

Re: Erfassung von Eigenschaften verhindern

Postby Support@SIB » Fri Nov 16, 2012 7:36 pm

Mit der doPreventEditDetails Methode erreicht man genau dieses Verhalten. Erst wenn der Master-Satz gültig gespeichert werden konnte wird ein eventAfterInserted gefeuert.

Falls gemeint ist, dass man dieses Verhalten auch bei einem Update haben will, funktioniert dies selbstverständlich auch mit eventAfterUpdating (die Master-Zeile wurde geändert) und eventAfterUpdated (die Master-Zeile wurde gültig gespeichert).

Die „ing“ Events teilen einem mit, dass eine Zeile geändert wurde, und gespeichert werden muss.
Die „ed“ Events teilen einem mit, dass die Zeile gültig gespeichert wurde.

Gegen unbeabsichtigtes Ändern kann man auch jederzeit z.B. einen Popup Dialog einblenden, der einem die Änderungen anzeigt, oder man stellt die geänderten Zeilen mit Hilfe des CellFormater Listeners unterschiedlich dar.

Als Source Beispiel für einen Lösch-Nachfrage Dialog:

Code: Select all
ProjXUtil.addAskDeleteDialog(databook);


Die Implementierung dahinter:
Code: Select all
private boolean bCheckDelete = false;
private IDataBook dbCheckDelete;

public void addAskDeleteDialog(IDataBook pDataBook)
{
      pDataBook.eventBeforeDeleting().addListener(this);
}

public void dataBookChanged(DataBookEvent pDataBookEvent) throws ModelException
{
      dbCheckDelete = pDataBookEvent.getChangedDataBook();
      RemoteWorkScreen workscreen = getRemoteWorkScreen(dbCheckDelete);
           
      if (workscreen != null && !bCheckDelete
          && dbCheckDelete.getWritebackIsolationLevel() != IDataBook.WriteBackIsolationLevel.DATASOURCE)
       {
            bCheckDelete = true;

            try
            {
                  ((RemoteWorkScreenApplication)workscreen.getApplication()).showQuestion(
                             this, "Are you sure to delete this row?", "doDelete", "doCancelDelete");
            }
            catch (Throwable throwable)
            {
                  throw new ModelException("Check delete Dialog could not be opened", throwable);
            }
            throw new SilentAbortException();
      }
}

public RemoteWorkScreen getRemoteWorkScreen(IDataBook pDataBook)
{
      IComponent comp = null;
      IControl[] dataBookControls = pDataBook.getControls();
         
      for (int i = 0; i < dataBookControls.length; i++)
      {
            if (dataBookControls[i] instanceof IComponent)
            {
                  comp = ((IComponent)dataBookControls[i]).getParent();
            }
      }
                                   
      while (comp != null && !(comp instanceof RemoteWorkScreen))
      {
            comp = comp.getParent();
      }
           
      return (RemoteWorkScreen)comp;
}

public void doDelete() throws Throwable
{     
      try
      {
            dbCheckDelete.delete();
      }
      finally
      {
            bCheckDelete = false;
      }
}
     
public void doCancelDelete() throws ModelException
{                       
      bCheckDelete = false;
}


Unserer Erfahrung nach ist der DATA_ROW WriteBackIsolationLevel der am meisten gewünschte Speichermodus von Kunden. Man hat nach jeder Zeile sofort die Gewissheit, dass alles korrekt eingegeben wurde und vor allem das alle Änderungen gespeichert wurden.

Im DATA_SOURCE Modus ist es immer wieder passiert dass ein Benutzer 10 und mehr Zeilen noch nicht gespeichert hat. Wenn er dann schnell die Applikation verlassen musste, sind Validierungsfehler aufgetreten, wie Unique Key, Gültigkeitsprüfungen, usw. Somit haben die Benutzer nur 2 Möglichkeiten: alles Verwerfen, weil sie wirklich eilig weg müssen, oder eine Zeile nach der anderen korrigieren, was natürlich dauern kann.

Im DATA_SOURCE Modus werden alle Änderungen einzeln commited, da JDBC Treiber standardmäßig auf Auto-Commit eingestellt sind.

Um dieses Verhalten zu ändern, muss man am Server folgende Funktionen in das Session Objekt einfügen:

Code: Select all
public void beginTransaction() throws Exception
{
    getDBAccess().getConnection().setAutoCommit(false);
}

public void commitTransaction() throws Exception
{
    try
    {
        getDBAccess().commit();
    }
    finally
    {
        getDBAccess().getConnection().setAutoCommit(true);
    }
}

public void rollbackTransaction() throws Exception
{
    try
    {
        getDBAccess().rollback();
    }
    finally
    {
        getDBAccess().getConnection().setAutoCommit(true);
    }
}

Speichern am Client:

Code: Select all
try
{
    getConnection().callAction(“beginTransaction“);
    getDataSource().saveAllDataBooks();
    getConnection().callAction(“commitTransaction“);
}
catch (Exception ex)
{
    getConnection().callAction(“rollbackTransaction“);
}


Sie können dies auch allgemein in der Application durchführen, z.B.:

Code: Select all
public void doSave() throws Exception
{
    …
}
User avatar
Support@SIB
 
Posts: 349
Joined: Mon Sep 28, 2009 1:56 pm

Re: Erfassung von Eigenschaften verhindern

Postby Support@SIB » Fri Nov 16, 2012 7:36 pm

Das Verhalten eines Detail Databooks, im Falle der Löschung, kann mit der Methode setDeleteCascade(boolean) gesteuert werden. Default ist false.

Es wird davon ausgegangen, dass in der Datenbank ein Cascade ForeignKey angelegt wurde, wenn es gewünscht ist, dass Detail Datensätze automatisch gelöscht werden sollen, ansonsten muss der Benutzer die Detail Datensätze manuell löschen. Sollte dieses Verhalten nicht gewünscht sein, werden bei setDeleteCascade(true) die Detail Datensätze automatisch von JVx gelöscht, wenn der Master Datensatz gelöscht wird.
User avatar
Support@SIB
 
Posts: 349
Joined: Mon Sep 28, 2009 1:56 pm


Return to Development (DE)