JPA und "Data-binding"

Servus,

ich bin jetzt also wieder zu JPA gewechselt, da es mir jetzt sinnvoller erscheint. Die bestehende Anwendung wurde in einzelne Module unterteilt, sodass man den persistence layer vollständig getrennt betrachten darf (fast).
Ich komm hier auf folgendes typisches Problem:
Wenn ich mir über den entityManager Objekte hole - diese darstelle in meinem View (JavaFX2 Anwendung) und es nun z.B. ändern möchte:

Ich hol mir das Objekt (sei es per entityManager Aufruf oder direkt aus der view-komponente), führe setze die Änderungen über die setter und führe ein “entityManager -> persist” aus.

Problem/Frage: Das Objekt / die Objekte im View die jetzt geändert wurden, werden nicht automatisch “refreshed”. Ist das Objekt z.B. an 2 Stellen dargestellt, so sollte es an beiden auch richtig verändert dargestellt werden. Dem ist genau derzeit nicht so!

Wie bekomm ich das jetzt am geschicktesten hin?
(Falls wir mal direkte SQL queries rauslassen die an der Datenbank manipulieren)
Ich dachte eigentlich das über JPA es möglich wäre das dies automatisch geschieht.

Muss ich jetzt nen EntityListener schreiben und dann nen Event-verschicken? (z.B. EventBus)

Gruß,
CHAOSFISCH

Also wenn ich das richtig verstehe, holst du dein Objekt aus der Datenbank, änderst es und schreibst es zurück? Ich gehe davon aus, dass das Objekt dann auch detached ist? Benutzt du die gleichen Objekte in der View, die du aus dem EntityManager bekommen hast oder hast du irgendwelche DTO’s / BO’s? Weil ein persist auf einem Detached- Objekt sollte eigentl. ne Exception auslösen.

Wenn du ein Objekt updaten willst solltest du merge verwenden. Merge gibt das Objekt zurück, welches in den PersistenzContext reattached wurde.
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#merge(T)

Die Aktualisierung von den View Entitys kannst ggf. mit Refresh(obj) durchführen. Das überschreibt dein Objekt mit der aktuellen Version aus der DB.
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#refresh(java.lang.Object)

[QUOTE=pl4gu33]Also wenn ich das richtig verstehe, holst du dein Objekt aus der Datenbank, änderst es und schreibst es zurück? Ich gehe davon aus, dass das Objekt dann auch detached ist? Benutzt du die gleichen Objekte in der View, die du aus dem EntityManager bekommen hast oder hast du irgendwelche DTO’s / BO’s? Weil ein persist auf einem Detached- Objekt sollte eigentl. ne Exception auslösen.

Wenn du ein Objekt updaten willst solltest du merge verwenden. Merge gibt das Objekt zurück, welches in den PersistenzContext reattached wurde.
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#merge(T)

Die Aktualisierung von den View Entitys kannst ggf. mit Refresh(obj) durchführen. Das überschreibt dein Objekt mit der aktuellen Version aus der DB.
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#refresh(java.lang.Object)[/QUOTE]

Es sind die Objekte (Entities), die direkt aus dem EntityManager kommen. Dazwischen befindet sich nichts.
Ich konnte eigentlich nie wirklich herausfinden wann ein Objekt jetzt als detached gilt.
Der persist aufruf beim update löst jedenfalls unter verwendung von eclipselink keine exception aus.

Dieser “Update-call” ist eigentlich nur ein Beispiel, welches zeigen soll, dass mein View halt dann nicht geupdated ist.
Klar kann ich wenn ein update geschieht refresh aufrufen, aber selbst dann muss ich dann ein event haben welches die views triggered -> entity listener?

In EclipseLink sollte es auch eine Exception geben, daher sieht es so aus, als ob du da eher nen neues Objekt speicherst. Ein Objekt ist detached wenn du es mit “detach(obj)” oder “clear()” aus dem PersistenzContext nimmst oder den EntityManager schließt.
http://www.prozesse-und-systeme.de/jpaEinleitung.html z.b. …

Du hast ja eigentl. das geänderte Objekt schon und gibst es ja nur zum Update in der DB wieder runter.
Ein wenig Code wäre vll. nicht schlecht. Dann wäre das ganze besser nachzuvollziehen und evtl. Fehler besser zu finden.

Detachted wird es z.B. auch, wenn Du den Entitymanger in einer EJB verwendest und diese dann über CDI irgendwo injectest.

[QUOTE=pl4gu33]In EclipseLink sollte es auch eine Exception geben, daher sieht es so aus, als ob du da eher nen neues Objekt speicherst. Ein Objekt ist detached wenn du es mit “detach(obj)” oder “clear()” aus dem PersistenzContext nimmst oder den EntityManager schließt.
http://www.prozesse-und-systeme.de/jpaEinleitung.html z.b. …

Du hast ja eigentl. das geänderte Objekt schon und gibst es ja nur zum Update in der DB wieder runter.
Ein wenig Code wäre vll. nicht schlecht. Dann wäre das ganze besser nachzuvollziehen und evtl. Fehler besser zu finden.[/QUOTE]
Also ich rufe weder detach noch clear auf.
Kurz zur Struktur der Anwendung:
Mehrere loosly coupled modules. Jedes könnte unter Beachtung seiner Abhängigkeiten einzelnd verwendet werden. Die “Kern-Module” verwenden POJOs. Sie wissen nicht wie und womit und ob ein persistence layer eingreift (Service Layer -> Dao Layer) dazwischen.
Zudem wird Dependency Injection mit Google Guice eingesetzt.
Dadurch ist es nun möglich ein eigenes “persistence”-Modul einzubauen, welches die persistence.xml und orm.xml enthält.
Die orm.xml ist notwendig da natürlich auf den POJOs der Module keine Annotations drinnen sind!
Soweit zur Struktur.

Google Guice bietet zwar eine eigene persistence lib an, diese scheint jedoch “fehlerhaft”. Die Einbindung läuft daher wie folgt:

	private static final String PERSISTENCE_UNIT_NAME = "persistenceUnit";

	private static final ThreadLocal<EntityManager> ENTITY_MANAGER_CACHE = new ThreadLocal<>();

     @Override
	protected void configure() {
               //............
        }

      @Provides
	@Singleton
	public EntityManagerFactory provideEntityManagerFactory() {
		return Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
	}

	@Provides
	@Singleton
	public EntityManager provideEntityManager(final EntityManagerFactory entityManagerFactory) {
		EntityManager entityManager = ENTITY_MANAGER_CACHE.get();
		if (null == entityManager) {
			ENTITY_MANAGER_CACHE.set(entityManager = entityManagerFactory.createEntityManager());
		}
		return entityManager;
	}

Dazu mal ein simples DAO als Beispiel:


	@Inject
	protected EntityManager entityManager;

	@Override
	public List<Account> getAll() {
		return (List<Account>) entityManager.createQuery("SELECT a FROM account a").getResultList();
	}

	@Override
	public Account get(final int id) {
		return entityManager.find(Account.class, id);
	}

	@Override
	@Transactional
	public void insert(final Account account) {
		entityManager.persist(account);
	}

	@Override
	@Transactional
	public void update(final Account account) {
		entityManager.persist(account);
	}

	@Override
	@Transactional
	public void delete(final Account account) {
		entityManager.remove(account);
	}
}```

Die Annotation @Transactional ist auch eine eigenkreation. Mithilfe von Guice hier ein MethodInterceptor der die Transaction beginned und commited oder rollback ausführt.

Beliebige stelle in einem ViewController:
```this.accountList = this.accountService.getAll();```

Beliebige 2te stelle: (nur Beispiel)
```Account account = this.accountService.getAll().get(0);
account.setName("Test");
accountService.update(account);```

Offenbar keine Exception, da Entity nicht detached, da EM nie geschlossen wurde.
Aber: Kein update im view (also z.B. in this.accountList) vorhanden.
Liegts hier etwa am ThreadLocal? - das wäre auch fraglich, da in dem Fall beides im JavaFX Application Thread zum Testen sich befand. Somit sollte es auch der selbe EM gewesen sein.

Hoffe das verdeutlicht mal das ganze.

Also wie jetzt?