Richtiger Umgang mit Verbindungen in JPA

DaBe1812

Bekanntes Mitglied
Hi,

ich habe hier ein Serverprojekt im WLP. Leider habe ich von Anfang an kein Hibernate richtig zum Laufen bekommen, weil die Config-Files im WLP scheinbar o anders hin müssen, als in allen anderen Servern. Jetzt haben wir aber wenigsten JPA am laufen. Ich bin mir aber bei der Stabilität, bzw. bei der Nutzung der Verbindungen nicht 100% sicher, ob ich da alles richtig mache, weil ich scheinbar ständig die Verbindungen offen lasse.

Deswegen brauche ich mal Experten-Rat:
Ich habe eine Klasse
Java:
@Singleton
public class JPAUtilities {

    private static EntityManagerFactory entityManagerFactory = null;
    
    public static EntityManagerFactory getEntityManagerFactory() {
        if(entityManagerFactory == null || !entityManagerFactory.isOpen()) {
            Map<String, Object> config = new HashMap<>();
            config.put("javax.persistence.jdbc.url", AtcConfiguration.dbParameter.getConnectionString());
            config.put("javax.persistence.jdbc.user", AtcConfiguration.dbParameter.getUSER());
            config.put("javax.persistence.jdbc.password", AtcConfiguration.dbParameter.getPASSWORD());
            config.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
            entityManagerFactory = Persistence.createEntityManagerFactory("oracle_con", config);
        }
        return entityManagerFactory;
    }
}

Wenn ich jetzt auf die Datenbank zugreifen will, dann sah das ungefähr so aus:
Java:
public static List<Abfrage> loadAllAbfragen() {
    EntityManagerFactory emf = JPAUtilities.getEntityManagerFactory();
    EntityManager em = emf.createEntityManager();
    TypedQuery<Abfrage> query = em.createQuery(StaticJPQL.ALLABFRAGEN, Abfrage.class);
    List<Abfrage> resultList = query.getResultList();
    em.close();
    return resultList;
}

Ich hab aber festgestellt, dass ich dann ein Problem bekomme, wenn ich auf einen gerade angelegten Satz direkt wieder zugreifen möchte. Also sieht eine neue Funktion jetzt so aus:
Java:
public static void setRunning(Abfrage a, boolean running) {
    EntityManagerFactory emf = JPAUtilities.getEntityManagerFactory();
    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    LastRun orgRun = em.find(LastRun.class, a.getPk());
    orgRun.setRunning(running);
    em.merge(orgRun);
    em.getTransaction().commit();
    em.close();
    emf.close();
}

Wenn ich den emf hier schließe, bekomme ich die Meldung, dass noch weitere X Verbindungen offen sind. wenn wir viel in einer Schleife schreiben wollten, dann sind wir auch oft an zu vielen offenen Verbindungen gescheitert, das habe ich dann als eigene Schleife in der Funktion erledigt, dann ging das auch.

Jetzt möchte ich nicht immer einen emf und em öffnen und am Ende wieder beides schließen, sondern hätte das gerne generell gebaut.

Habe ich hier schon den Ansatz verbockt, oder habe ich einfach irgendwo einen Denkfehler gehabt?

Danke für eure Hilfe.
 

Oneixee5

Top Contributor
Ich meine: so sollte das nicht aussehen. Deine Anwendung sollte die Connections aus dem ConnectionPool/Datasource des Servers holen und nicht selbst verwalten. daher kommt auch das Problem mit den "zu vielen offenen Verbindungen" und die Geschwindigkeit leidet.
Java:
            Map<String, Object> config = new HashMap<>();
            config.put("javax.persistence.jdbc.url", AtcConfiguration.dbParameter.getConnectionString());
            config.put("javax.persistence.jdbc.user", AtcConfiguration.dbParameter.getUSER());
            config.put("javax.persistence.jdbc.password", AtcConfiguration.dbParameter.getPASSWORD());
Wie das beim WLP genau aussehen muss kann ich dir nicht sagen. Beim WLS werden die benötigten Infos in einer XML-Datei hinterlegt und einfach über den Namen als Ressource geladen. Das sieht dann ungefähr so aus:
Java:
    private static final String PERSISTENCE_UNIT_NAME = "YOUR_DATASOURCE_NAME";

    private static EntityManagerFactory buildEntityManagerFactory() {
        final Map<String, String> map = Collections.singletonMap(PersistenceUnitProperties.LOGGING_LOGGER, SESSION_CUSTOMIZER);
        try {
            return Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, properties);
        } catch ...
          
        }
    }
Angaben zu Connections, Datenbanken, Passwort, etc. kennt die Anwendung gar nicht und werden vom Admin des Servers verwaltet. Die Anwendung muss nur den Namen der Ressource (Datasource) kennen. Dieser liegt in einer XML-Datei als Standardwert und kann auch auch vom Admin beeinflusst werden ohne an der Anwendung selbst etwas zu ändern. Die Anwendung kennt nur den Namen der Ressource in der XML-Datei.
XML:
...
    <wls:resource-description>
        <wls:res-ref-name>jdbc/YOUR_DATASOURCE_NAME</wls:res-ref-name>
        <wls:jndi-name>jdbc/Datasource_Name_From_Server</wls:jndi-name>
    </wls:resource-description>
...
 
Zuletzt bearbeitet:

DaBe1812

Bekanntes Mitglied
Hi,

sowas hatte ich hier auch schon mal diskutiert, dass eigentlich der WLP meine Verbindungen verwalten sollte (allein schon, weil es bei jedem Server-Start im Log steht). Ich habe aber keine Kontrolle über den Server, d.h. ich baue mein ear und habe noch die Möglichkeit ein paar Files in einen vorgegebenen Ordner schieben zu lassen.
Die Config für den Oracle Server ist in jedem Stage anders und ich kann diese nur anpassen, in dem ich die zusätzlichen Files patchen lasse (NEAT). D.h. ich müsste die Config.xml irgendwo in diesen Pfad werfen und dann dem System erklären, dass es bitte da seine Configs ziehen soll. Hat aber irgendwie noch nicht funktioniert, aber vielleicht hat da einer von euch noch nen Ansatz.

Und wenn das dann alles geklappt haben sollte, wie muss ich mir meinen Code dann vorstellen? Ändere ich bei mir nur die JPAUtilities so ab, wie du geschrieben hast und der Rest bleibt dann, so wie er ist, ohne dass ich alles immer closen muss? Oder hätte ich das Close sowieso einbauen müssen?
 

Oneixee5

Top Contributor
Gibt es beim WLP keine Profile oder Pläne? So das man in den Konfigurationen des EAR-Files Variablen oder Defaults für mehreren Umgebungen nutzen kann. Der Admin oder Du erstellst dann ein Profil für jeden Server und das EAR ist immer gleich. Beim Deployment wird dann einfach das EAR mit dem Profil zusammen verwendet. Wir haben auch immer X - Server und machen das schon ewig so. Kontrolle über die Prod- und VorProd-Server haben wir auch nicht, schon gar nicht über die Cluster oder Kundensysteme.
 

Oneixee5

Top Contributor
Und wenn das dann alles geklappt haben sollte, wie muss ich mir meinen Code dann vorstellen? Ändere ich bei mir nur die JPAUtilities so ab, wie du geschrieben hast und der Rest bleibt dann, so wie er ist, ohne dass ich alles immer closen muss? Oder hätte ich das Close sowieso einbauen müssen?
Der eigentlich favorisierte Weg wäre es, die Datasource in die persistence.xml einzubinden. Man benötigt normalerweise gar keinen Java-Code dafür. Den EntityManager holt man sich dann per JNDI oder via @EJB Injection. Den Umweg über Code musste man früher mal machen, als JPA noch nicht so viele Möglichkeiten hatte. Im Notfall musste man sich noch mal eine JDBC-Connection holen, um ein paar spezielle Sachen hinzubekommen. In aktuellen Versionen ist das normalerweise nicht mehr nötig und eigentlich ziemlich kritisch.
Zum Thema: close, finde ich diese Antwort ganz gut: https://stackoverflow.com/questions/10762974/should-jpa-entity-manager-be-closed
 

DaBe1812

Bekanntes Mitglied
Hi, ich hab gestern die Diskussion bei den Admins mal wieder losgetreten. Allein schon, weil ich mir am Wochenende endlich mal das Java EE 8 Buch von Rheinwerk geleistet habe und da ein paar Ansätze drin waren, die ich dann weiter verfolgen konnte.
Also beim WLP muss ich das JPA-2.2 Feature aktivieren. Der benutzt dann Eclips-Link.
Wegen der Konfiguration redet grade A-Hörnchen mit B-Hörnchen, wo die letztendlich landen soll. Zum parametrieren der Umgebungen nutzen die hier NEAT, das tauscht dann definierte Config-Teile beim Deployment aus.

Aktuell ist die Umsetzung halt eher so ne Mischung aus JPA und Oldschool, aber da wir immer mehr Entwickler an dem Projekt werden, wäre es schön, wenn ich einfach jedem sagen könnte, dass er sich mit seinem DB-ram an die JPA halten soll und dann läuft das.

Außerdem will ich nicht vor jedem neuen Deployment vorher erstmal die Datenbank anpassen müssen, weil ne neue Tabelle oder Spalte dazu gekommen ist, dass kann JPA schön selber machen.
 

DaBe1812

Bekanntes Mitglied
Hi,

nachdem ich erstmal mit dem Release der nächsten Version beschäftigt war, kann ich mich auch wieder dem Thema widmen. Und ich bin (glaube ich) um einiges weiter gekommen.
Also in der server.xml kam folgendes hinzu:
XML:
<library id="OracleLib">
    <fileset dir="${shared.resource.dir}/ospe-lib" includes="ojdbc8.jar"/>
</library>

<jdbcDriver id="OracleJDBC" libraryRef="OracleLib"/>

<dataSource jndiName="jdbc/atcdb" transactional="false">
    <connectionManager maxPoolSize="300" minPoolSize="20"/>
    <jdbcDriver libraryRef="OracleLib"/>
    <properties.oracle databaseName="DB123" driverType="thin" portNumber="1521" serverName="Externer.Server" user="dbBenutzer" password="geheim"/>
</dataSource>

Jetzt versuche ich das in meinem Code auf zu rufen, nach viel Try-And-Error und lesen vieler Beiträge habe ich eine Tools.java:
Java:
import java.util.List;

import javax.enterprise.context.Dependent;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Dependent
public class Tools {

    @PersistenceContext(unitName = "jdbc/atcdb")
    private EntityManager em;
    
    protected boolean correctNumRecords(String table, String where, Integer expectedNum) {
        String sql = "SELECT COUNT(*) FROM " + table + " WHERE " + where;
        Query q = em.createNativeQuery(sql);
        int count = ((Number) q.getSingleResult()).intValue();
        
        if(count == expectedNum) {
            return true;
        }
        return false;
    }

    protected int deleteOldRecords(String table, String where) {
        String sql = "DELETE FROM " + table + " WHERE " + where;
        Query q = em.createNativeQuery(sql);
        return q.executeUpdate();
    }
...   
}

Nach noch mehr suchen und weinen und lesen wird diese in meinem DatabaseCheck.java folgendermaßen verwendet:
Java:
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.DependsOn;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Startup
@Singleton
@DependsOn("AtcConfiguration")
public class DatabaseCheck {

    private static final Logger LOG = LogManager.getLogger(DatabaseCheck.class);
    
    @Inject
    private Tools tools;
    
    @PostConstruct
    public void makeDatebaseCheck() {
        long start = System.currentTimeMillis();
        
        /*
         * Neue Werte für das KV-Modul von Alina, damit die Datenbankverbindung im Prod kunfigurierbar ist
         */
        boolean kvSetsPresent = tools.correctNumRecords("Atc_Parameter"
                ,"STATUS='ACTIVE' AND KEY1='SERVER' AND KEY2='KV'"
                ,kvDbParameterArr.size());
        ...
    }
}
Jetzt schmiert mir der Server beim Starten ab, wenn er versucht die Klasse zu laden. Meldungen sind:
Code:
[ERROR   ] CWWJP0029E: Der Server kann die Persistenzeinheit jdbc/atcdb im Modul AssetToConnector-0.0.1-SNAPSHOT.war und in der Anwendung AssetToConnector nicht finden.
[ERROR   ] CWNEN0035E: Die Referenz java:comp/env/main.java.configuration.cache.databaseCheck.Tools/em des Typs javax.persistence.EntityManager f�r die Komponente null im Modul AssetToConnector-0.0.1-SNAPSHOT.war der Anwendung AssetToConnector kann nicht aufgel”st werden.
[ERROR   ] CWOWB1000E: Es ist ein CDI-Fehler aufgetreten: The java:comp/env/main.java.configuration.cache.databaseCheck.Tools/em reference of type javax.persistence.EntityManager for the null component in the AssetToConnector-0.0.1-SNAPSHOT.war module of the AssetToConnector application cannot be resolved.
Ich habe in meinem Klassenpfad keine persistence.xml, weil die Konfiguration doch eigentlich im Server passiert, oder ist das schonmal ein Fehler?
Außerdem habe ich die EclipseLink-Klassen noch nicht in den Klassenpfad gepackt.
Das Feature jpa-2.2 ist im Server aktiviert.
 

LimDul

Top Contributor
Die persistence.xml brauchst du. Weil die gibt für deine Anwendung an, wie die persistence aussieht. In der Persistence.xml wiederum verweist du dann an den entsprechenden Stellen auf die Server-Resourcen.

Siehe z.B: https://thorben-janssen.com/jpa-persistence-xml/

Im Server konfigurierst du die Datsource und in der persistence.xml sagst du "Nutze die JTA Datasource XY"
 

DaBe1812

Bekanntes Mitglied
Okay, hab ich so rein genommen, kommt aber immer noch zum selben Fehler. Persistence.xml sieht folgendermaßen aus:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="atcdb" transaction-type="JTA">
        <jta-data-source>jdbc/atcdb</jta-data-source>
    </persistence-unit>
</persistence>
Und liegt unter WebContent\META-INF

In die ear hab ich auch geschaut, da liegt sie sowohl direkt in der ear im META-INF, als auch in der war unter META-INF. Kann ich irgendwie debuggen, ob er das File überhaupt findet/lädt?
 

DaBe1812

Bekanntes Mitglied
Jepp, my fault, hatte ich nach dem erstellen der persistence.xml in der tools.java geändert, also da steht jetzt:
Java:
@PersistenceContext(unitName = "atcdb")
private EntityManager em;
Und die Fehlermeldung sagt natürlich auch entsprechend
Code:
[ERROR   ] CWWJP0029E: Der Server kann die Persistenzeinheit atcdb im Modul AssetToConnector-0.0.1-SNAPSHOT.war und in der Anwendung AssetToConnector nicht finden.
 

mihe7

Top Contributor
Und liegt unter WebContent\META-INF
ear im META-INF, als auch in der war unter META-INF
Nein, im war unter WEB-INF/classes/META-INF, in einem EJB-jar unter /META-INF:

https://docs.oracle.com/javaee/6/tutorial/doc/bnbqw.html#bnbrj hat gesagt.:
Persistent units can be packaged as part of a WAR or EJB JAR file or can be packaged as a JAR file that can then be included in an WAR or EAR file.
  • If you package the persistent unit as a set of classes in an EJB JAR file, persistence.xml should be put in the EJB JAR’s META-INF directory.
  • If you package the persistence unit as a set of classes in a WAR file, persistence.xml should be located in the WAR file’s WEB-INF/classes/META-INF directory.
  • If you package the persistence unit in a JAR file that will be included in a WAR or EAR file, the JAR file should be located in either
    • The WEB-INF/lib directory of a WAR
    • The EAR file’s library directory
 

DaBe1812

Bekanntes Mitglied
Hi,
hab es jetzt selbst da eingefügt, und es bleibt beim selben Fehler. Hab ich in meinem Projekt evtl. vergessen irgend eine Klasse rein zu packen, die erst dazu führt, dass in diesem Ordner geschaut wird, oder überhaupt geschaut wird.
 

mihe7

Top Contributor
An Deiner Stelle würde ich mir lokal einen Application Server draufklatschen und damit testen. Wenns da klappt -> Admin.
 

DaBe1812

Bekanntes Mitglied
Hi,
ich habe den AppicationServer lokal liegen. Also wenn ich an noch einer Stelle schauen kann, sag mir wo.
Grundsätzlich versuche ich ja gerade es lokal hin zu bekommen, damit ich dem Admin sagen kann, was er im Remote ApplicationServer einstellen muss, damit meine persistence.xml sauber arbeiten kann.
Damit da endlich mal was voran geht, würde ich auch das Projekt nochmal neu aufsetzen, aber mir muss jemand sagen wo ich ansetzen sollte.
 

DaBe1812

Bekanntes Mitglied
Nope, Eclipse mit einem Projekt, WLP als Server im Eclipse hinterlegt, aber aus irgendwelchen mir unerfindlichen Gründen muss ich das Projekt immer erst mit ANT kompilieren, um dann die ear in WLP aus zu führen.
 

mihe7

Top Contributor
Ok, Anbei ein kleines Beispielprojekt mit Maven und Payara-Micro (musst Du Dir halt runterladen, ist nur ein Jar).

Konfiguration in create-datasource.script; aktuell für MySQL. Musst Du halt an Dein databaseName="DB123" driverType="thin" portNumber="1521" serverName="Externer.Server" user="dbBenutzer" password="geheim"/> anpassen.

run.sh ist selbsterklärend, da musst Du eben zwei Variablen anpassen bzw. für Windows umschreiben.

Wenn das Ding problemlos startet, sollte es möglich sein:

Code:
curl http://localhost:8080/jpatest-1.0-SNAPSHOT/resources/sql?q=<SQL>

aufzurufen. Achtung: <SQL> ist ein beliebiger SQL-String:
Code:
curl http://localhost:8080/jpatest-1.0-SNAPSHOT/resources/sql?q=SELECT%2032*32
liefert also 1024.

Müsste eigentlich auch im Browser funktionieren.

Wenn Du das funktionierend für Deine Oracle-DB hinbekommst, würde ich das Teil mal auf dem WLP deployen. Dort geht es ja dann nur noch um die Konfiguration der Datasource. Wenn das läuft, sollte das problemlos auf Deine Anwendung übertragbar sein.
 

Anhänge

  • jpatest.zip
    3,6 KB · Aufrufe: 1

DaBe1812

Bekanntes Mitglied
Also als erstes danke, dass du noch nicht total entnervt mir einen Kurs oder ein Buch angeboten hast, das habe ich nämlich hinter mir und beides hilft nicht bei konkreten Problemen.
Zum zweiten, dein Programm funktioniert, wenn ich über den Browser
HTTP:
http://localhost:8080/jpatest-1.0-SNAPSHOT/resources/sql?q=SELECT%2032*32%20FROM%20DUAL
aufrufe, dann bekomme ich 1024 als Antwort. Oracle braucht immer eine Tabelle zum selecten, und die ist dann halt dual.
Was ich bis dahin ändern musste:
In create-datasource.script
Code:
--datasourceclassname oracle.jdbc.pool.OracleDataSource
resources.jdbc-connection-pool.demo-pool.property.url=jdbc:oracle:thin:@server.name.intern:1521/dbName
Letzteres, weil er mit sonst die URL falsch generiert.

Also habe ich das ganze jetzt dem WLP zum frass vorgeworfen und erhalte noch immer einen Fehler, weil die Ressource null ist.
In der server.xml ist folgendes konfiguriert:
XML:
<server description="new server">

    <!-- Enable features -->
    <featureManager>
        <feature>javaee-8.0</feature>
        <feature>localConnector-1.0</feature>
        <feature>jpa-2.2</feature>
    </featureManager>

    <include location="${shared.resource.dir}/keystores/default_ssl.xml" optional="false"/>
    
    <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
    <httpEndpoint httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>
                  
    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>

    <!-- Default SSL configuration enables trust for default certificates from the Java runtime -->
    <ssl id="defaultSSLConfig" trustDefaultCerts="true"/>

    <applicationMonitor updateTrigger="mbean"/>

    <dataSource jndiName="jdbc/myDB" type="javax.sql.ConnectionPoolDataSource">
        <jdbcDriver>
            <library>
                <fileset dir="${shared.resource.dir}/oracle"></fileset>
            </library>
        </jdbcDriver>
        <properties.oracle URL="jdbc:oracle:thin:@mein.server.intern:1521/meineDB" password="{xor}ByUwJgY+CDRoGhU=" user="meinUser"></properties.oracle>
    </dataSource>

    <webApplication id="jpatest" location="jpatest-1.0-SNAPSHOT.war" name="jpatest"/>
</server>

Ich denke der dataSource Teil ist entweder ganz, oder teilweise falsch, aber ich bekomme auch nur schwammige Ergebnisse, wenn ich dafür im WLP einen Anleitung suche.
 

mihe7

Top Contributor
Probier mal etwas in der Art (fileset ggf. anpassen):
XML:
<library id="OracleJDBC">
    <fileset dir="${shared.resource.dir}" includes="ojdbc*.jar"/> 
</library>
<dataSource id="myDB" jndiName="jdbc/myDB">
    <jdbcDriver libraryRef="OracleJDBC" />
    <properties.oracle URL="jdbc:oracle:thin:@mein.server.intern:1521/meineDB" password="{xor}ByUwJgY+CDRoGhU=" user="meinUser" />
</dataSource>
 

DaBe1812

Bekanntes Mitglied
Moin,
ich hab die dataSource jetzt umgestellt mit ein paar Änderungen:
XML:
    <library id="OracleJDBC">
        <fileset dir="${shared.resource.dir}/oracle" includes="ojdbc8.jar"/>
    </library>
    
    <dataSource id="myDB" jndiName="jdbc/myDB" type="javax.sql.ConnectionPoolDataSource">
        <connectionManager maxPoolSize="20" minPoolSize="5" connectionTimeout="10s" agedTimeout="30m" />
        <jdbcDriver libraryRef="OracleJDBC" />
        <properties.oracle URL="jdbc:oracle:thin:@mein.server.intern:1521/meineDB" password="{xor}ByUwJgY+CDRoGhU=" user="meinUser" />
    </dataSource>
trotzdem kommt es zu folgendem Fehler:
Code:
[ERROR   ] CWWJP0029E: Der Server kann die Persistenzeinheit myPU im Modul jpatest-1.0-SNAPSHOT.war und in der Anwendung jpatest nicht finden.
[ERROR   ] CWNEN0035E: Die Referenz java:comp/env/jpatest.SqlService/em des Typs javax.persistence.EntityManager f�r die Komponente null im Modul jpatest-1.0-SNAPSHOT.war der Anwendung jpatest kann nicht aufgel”st werden.
[ERROR   ] CWOWB1000E: Es ist ein CDI-Fehler aufgetreten: The java:comp/env/jpatest.SqlService/em reference of type javax.persistence.EntityManager for the null component in the jpatest-1.0-SNAPSHOT.war module of the jpatest application cannot be resolved.
ich habe am war-file seit es in deiner config funktioniert hat nichts mehr gemacht. Es ist also genau dieselbe Version, wie im Test auf dem lokalen Server.
 

DaBe1812

Bekanntes Mitglied
Start von testJPA (WebSphere Application Server 22.0.0.5/wlp-1.0.64.cl220520220425-0301) auf IBM J9 VM, Version 8.0.7.6 - pwa6480sr7fp6-20220330_01(SR7 FP6) (de_DE)
 

mihe7

Top Contributor
Sorry, bei uns ist seit Vormittag Netz-Totalausfall, bin nur behelfsmäßig online. Hätte eigentlich bis 16:00 Uhr erledigt sein sollen - tja, hätte... Sobald ich wieder vernünftiges Netz habe, melde ich mich wieder.
 

mihe7

Top Contributor
Also, ich habe jetzt einen Docker-Container aufgesetzt und mit H2-Databas probiert:
Das Dockerfile:
Code:
FROM ibmcom/websphere-liberty:22.0.0.5-full-java8-openj9-ubi
ADD h2-2.1.214.jar /opt/ibm/wlp/usr/shared/resources/
ADD server.xml /opt/ibm/wlp/usr/servers/defaultServer/

Die server.xml (das ist einfach die mitgelieferte, ergänzt um die DataSource-Geschichten:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<server description="Default server">

    <!-- Enable features -->
    <featureManager>
        <feature>javaee-8.0</feature>
    <feature>microProfile-3.0</feature>
    </featureManager>

    <library id="OracleJDBC">
        <fileset dir="${shared.resource.dir}" includes="h2*.jar"/> 
    </library>
    <dataSource id="myDB" jndiName="jdbc/myDB" type="javax.sql.ConnectionPoolDataSource">
        <jdbcDriver
            javax.sql.ConnectionPoolDataSource="org.h2.jdbcx.JdbcDataSource"
            javax.sql.DataSource="org.h2.jdbcx.JdbcDataSource"
            javax.sql.XADataSource="org.h2.jdbcx.JdbcDataSource"
            libraryRef="OracleJDBC"/>
        <properties URL="jdbc:h2:~/test" />
</dataSource>   

    <!-- This template enables security. To get the full use of all the capabilities, a keystore and user registry are required. -->
    
    <!-- For the keystore, default keys are generated and stored in a keystore. To provide the keystore password, generate an 
         encoded password using bin/securityUtility encode and add it below in the password attribute of the keyStore element. 
         Then uncomment the keyStore element. -->
    <!--
    <keyStore password=""/> 
    -->
    
    <!--For a user registry configuration, configure your user registry. For example, configure a basic user registry using the
        basicRegistry element. Specify your own user name below in the name attribute of the user element. For the password, 
        generate an encoded password using bin/securityUtility encode and add it in the password attribute of the user element. 
        Then uncomment the user element. -->
    <basicRegistry id="basic" realm="BasicRealm"> 
        <!-- <user name="yourUserName" password="" />  --> 
    </basicRegistry>
    
    <!-- To allow access to this server from a remote client host="*" has been added to the following element -->
    <httpEndpoint id="defaultHttpEndpoint"
                  host="*"
          httpPort="9080"
                  httpsPort="9443" />
                  
    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>

</server>

Der h2-Treiber ist einfach aus dem Netz gezogen.
Code:
# Doker Image erstellen
docker build -t wsa .

# Container ausführen
docker run --rm -p9080:9080 --name wsa -d -v $(pwd)/jpatest-1.0-SNAPSHOT.war:/opt/ibm/wlp/usr/servers/defaultServer/dropins/jpatest-1.0-SNAPSHOT.war wsa

docker logs liefert unter anderem:
Code:
Launching defaultServer (WebSphere Application Server 22.0.0.5/wlp-1.0.64.cl220520220425-0301) on Eclipse OpenJ9 VM, version 1.8.0_332-b09 (en_US)
...
[AUDIT   ] CWWKZ0001I: Application jpatest-1.0-SNAPSHOT started in 3.299 seconds.
...

Beim Abruf bekomme ich zwar Exceptions (NullPointerException, augenscheinlich weil ich keine Entity-Klassen habe) ABER die Persistence-Unit wird gefunden und mit der DB klappt es auch (das weiß ich, weil ich a) ein paar Anläufe brauchte, bis ich soweit war und b) die DB angelegt wird).

Mehr kann ich aktuell auch nicht dazu sagen.
 

DaBe1812

Bekanntes Mitglied
Moin, hatte heute auch erst wenig Zeit danach zu schauen.
Die Features im WLP habe ich jetzt so umgestellt, wie du geschrieben hast. Hat aber auch keinen Erfolg geracht. Ich hab jetzt mal die gesamte Fehlermeldung drunter gepackt:
Code:
[AUDIT   ] CWWKT0016I: Webanwendung verf�gbar: (default_host): http://localhost:9080/jpatest/
[AUDIT   ] CWWKZ0001I: Die Anwendung jpatest ist nach 34,356 Sekunden gestartet.
[WARNUNG ] CWMOT0010W: OpenTracing kann keine JAX-RS-Anforderungen verfolgen, da keine OpentracingTracerFactory-Klasse bereitgestellt wurde oder Clientbibliotheken f�r das Tracing-Back-End-Programm nicht im Klassenpfad enthalten sind.
[ERROR   ] CWWJP0029E: Der Server kann die Persistenzeinheit myPU im Modul jpatest-1.0-SNAPSHOT.war und in der Anwendung jpatest nicht finden.
[ERROR   ] CWNEN0035E: Die Referenz java:comp/env/jpatest.SqlService/em des Typs javax.persistence.EntityManager f�r die Komponente null im Modul jpatest-1.0-SNAPSHOT.war der Anwendung jpatest kann nicht aufgel”st werden.
[ERROR   ] CWOWB1000E: Es ist ein CDI-Fehler aufgetreten: The java:comp/env/jpatest.SqlService/em reference of type javax.persistence.EntityManager for the null component in the jpatest-1.0-SNAPSHOT.war module of the jpatest application cannot be resolved.
[ERROR   ] CWOWB1000E: Es ist ein CDI-Fehler aufgetreten: The java:comp/env/jpatest.SqlService/em reference of type javax.persistence.EntityManager for the null component in the jpatest-1.0-SNAPSHOT.war module of the jpatest application cannot be resolved.
[ERROR   ] CWWJP0029E: Der Server kann die Persistenzeinheit myPU im Modul jpatest-1.0-SNAPSHOT.war und in der Anwendung jpatest nicht finden.
[ERROR   ] CWNEN0035E: Die Referenz java:comp/env/jpatest.SqlService/em des Typs javax.persistence.EntityManager f�r die Komponente null im Modul jpatest-1.0-SNAPSHOT.war der Anwendung jpatest kann nicht aufgel”st werden.
[ERROR   ] CNTR0019E: EJB hat eine unerwartete (nicht deklarierte) Ausnahme beim Aufruf der Methode "getResult" ausgel”st. Ausnahmedaten: javax.ejb.EJBException: The java:comp/env/jpatest.SqlService/em reference of type javax.persistence.EntityManager for the null component in the jpatest-1.0-SNAPSHOT.war module of the jpatest application cannot be resolved.
    at com.ibm.wsspi.injectionengine.InjectionBinding.getInjectionObject(InjectionBinding.java:1495)
    at [internal classes]
    at jpatest.EJSLocalNSLSqlService_49731880.getResult(EJSLocalNSLSqlService_49731880.java)
    at sun.reflect.GeneratedMethodAccessor1286.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Method.java:508)
    at com.ibm.ws.jaxrs20.ejb.JaxRsFactoryBeanEJBCustomizer.serviceInvoke(JaxRsFactoryBeanEJBCustomizer.java:335)
    at [internal classes]

[WARNUNG ] CWMOT0009W: Der OpenTracing Exception Mapper hat eine unbehandelte Ausnahme der JAX-RS-Anwendung erkannt und behandelt diese.
                                                                                                               javax.ejb.EJBException: The java:comp/env/jpatest.SqlService/em reference of type javax.persistence.EntityManager for the null component in the jpatest-1.0-SNAPSHOT.war module of the jpatest application cannot be resolved.
    at com.ibm.wsspi.injectionengine.InjectionBinding.getInjectionObject(InjectionBinding.java:1495)
    at [internal classes]
    at jpatest.EJSLocalNSLSqlService_49731880.getResult(EJSLocalNSLSqlService_49731880.java)
    at sun.reflect.GeneratedMethodAccessor1286.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Method.java:508)
    at com.ibm.ws.jaxrs20.ejb.JaxRsFactoryBeanEJBCustomizer.serviceInvoke(JaxRsFactoryBeanEJBCustomizer.java:335)
    at [internal classes]

Außerdem starte ich die Anwendung direkt aus dem Eclipse, ist das vielleicht auch ein Fehler, bzw. eine Fehlerquelle?
 

mihe7

Top Contributor
So, hab gerade noch eine Entity hinzugefügt und schon:
Code:
curl -i http://localhost:9080/jpatest-1.0-SNAPSHOT/resources/sql?q=SELECT%2032*32%20FROM%20DUAL
HTTP/1.1 200 OK
X-Powered-By: Servlet/4.0
Content-Type: application/octet-stream
Date: Mon, 11 Jul 2022 15:05:25 GMT
Content-Language: en-US
Content-Length: 4

1024
 

DaBe1812

Bekanntes Mitglied
Tatsächlich. Wenn ich die Anwendung als WAR exportiere und manuell in den Pfad im Websphere ablege, dann geht es. Jetzt muss es quasi nurnoch in meiner "großen" Anwendung funktionieren, dann bin ich fast glücklich. Weil debugging geht jetzt aktuell nicht mehr so richtig
 

mihe7

Top Contributor
Zu Eclipse kann ich kaum etwas sagen, aber hier im Forum weiß sicher jemand (z. B. @LimDul, @KonradN), was in Eclipse zu tun ist (vermutlich irgendeine Run Configuration aber... da halt ich mich raus ;))
 

DaBe1812

Bekanntes Mitglied
Ja moin auch. Also erstmal die positiven Aspekte. Ich hab gestern noch die eigentliche Anwendung als WAR-File exportiert und in den WLP geworfen und es läuft. Eine Erstellung des WAR-File via ANT hat übrigens nicht geklappt. Heute werde ich mal schauen, was an den WAR-Files unterschiedlich ist.
@LimDul kreativer Ansatz, ich kann tatsächlich mal nachfragen, ob es hier die Möglichkeit für IntelliJ gibt, das hab ich jetzt schon mal an einigen Ecken gehört. Das ist aber im JavaEE Bereich bei mir Neuland, da hab ich vor Jahren mal eine erste App mit gebaut, weil ich wissen wollte, wie das geht.
Also siehst du bei Eclipse keine Chance, oder steht da der Aufwand einfach nur in keinem Verhältnis zum Nutzen.
 

LimDul

Top Contributor
Ich sag mal so, ich bin Eclipse Nutzer seit Ewigkeiten (und auch auf der Arbeit aufgrund der Abhängigkeiten zu einem Eclipse Plugin). Aber mittlerweile nervt mich Eclipse nur noch.

Wir entwickeln auch JavaEE, allerdings auf einem Jboss. Ich deploye schon seit Ewigkeiten nur das WAR File unserer Anwendung, weil das Deployment aus Eclipse immer wieder Probleme macht. Es sind Klassen doppelt oder (wie bei dir) Probleme mit der Persistence. Das lässt sich meist durch ein Full Publish + ggf. komplettes Maven Update / Maven Build lösen. Aber das ist extrem lästig und kostet dementsprechend Zeit.

Meine Lösung ist daher ein Shell-Skrip, was grob wie folgt aussieht:
Code:
#!/bin/bash
mvn -T 4C -f myProject $1 install -DskipTests -Dspotbugs.skip=true -Dcheckstyle.skip=true
cp myProject/webapplication/target/webapplication.war /c/Development/jboss/jboss-7.3/standalone/deployments/

Das geht recht fix trotz 46 Maven Modulen
 

LimDul

Top Contributor
Build (im Zug):
[INFO] Total time: 01:58 min (Wall Clock)

Allerdings ohne Clean mit einer Änderung an einer Handvoll Dateien.
 

LimDul

Top Contributor
Problem mit Zug ist - da ist Vaadin 23 drin und das tut im Build-Prozess merkwürdige Dinge, ich hab keine Ahnung wie sehr der versucht sich ins Intranet zu verbinden. Und deutsche Bahn + Internet ist manchmal "interessant"
 

DaBe1812

Bekanntes Mitglied
Hi,
also mittlerweile wird es komisch (nicht im Sinne von Haha):
Ich hab die Anwendung jetzt einfach wieder in Eclipse im Server geadded. Gestartet (weil ich den Fehler nochmal weiter geben wollte) und tada, der Punkt läuft. Dann hab ich mir gesagt, dass sind einfach noch Lebenszeichen von vorher, deswegen geht das. Also am Code rumgeschraubt und einfach mal eine andere Klasse, die mit Persistence arbeitet auf
Java:
    @PersistenceContext(unitName = "atcdb")
    private EntityManager em;
umgestellt. Dann die ganzen Funktionen da drin umgebaut (also den überflüsigen emf- und em-Kram am Anfang jeder Methode entfernt) und allen Methoden das static genommen.
Dann versucht zu kompilieren und erstmal alle daraus entstandenen Bugs entfernt. Die erste Klasse, mit der ich getestet habe, geht immer noch, aber die anderen Klassen bekommen irgendwie das Inject nicht hin. Ich habe auch nochmal das Projekt als WAR exportiert, kommt aber auf denselben Fehler.

Die Klasse mit der ich getestet habe sieht wie folgt aus:
Java:
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.DependsOn;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Startup
@Singleton
@DependsOn("AtcConfiguration")
public class DatabaseCheck {

    private static final Logger LOG = LogManager.getLogger(DatabaseCheck.class);
    
    @Inject
    private Tools tools;
    
    @PostConstruct
    public void makeDatebaseCheck() {
        long start = System.currentTimeMillis();
        
        /*
         * Neue Werte für das KV-Modul von Alina, damit die Datenbankverbindung im Prod kunfigurierbar ist
         */
        boolean kvSetsPresent = tools.correctNumRecords("Atc_Parameter"
                ,"STATUS='ACTIVE' AND KEY1='SERVER' AND KEY2='KV'"
                ,kvDbParameterArr.size());
Wenn ich in Zeile 25 debugge, dann ist Tools nicht null und der em in Tools ist irgend ein tx... Objekt.
Hier nochmal Tools dazu:
Java:
import java.util.List;

import javax.enterprise.context.Dependent;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Dependent
public class Tools {

    @PersistenceContext(unitName = "atcdb")
    private EntityManager em;
    
    protected boolean correctNumRecords(String table, String where, Integer expectedNum) {
        String sql = "SELECT COUNT(*) FROM " + table + " WHERE " + where;
        Query q = em.createNativeQuery(sql);
        int count = ((Number) q.getSingleResult()).intValue();
        
        if(count == expectedNum) {
            return true;
        }
        return false;
    }
Die beiden funktionieren. Aber Weiter im Code geht das Prinzip irgendwie nicht mehr auf. Ich habe eine Klasse als Cache:
Java:
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.DependsOn;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import main.java.atc.definitions.ReferenceSystem;
import main.java.configuration.States;
import main.java.configuration.cache.kvdb.KVDbConfigs;

@Startup
@Singleton
@DependsOn({"AtcConfiguration", "DatabaseCheck"})
public class ServerCache {
    
    private static final Logger LOG = LogManager.getLogger(ServerCache.class);
    private static final Integer RELOAD = 15;
    
    private static ServerCache cache = new ServerCache();
    private static Calendar lastScan = Calendar.getInstance();

    private SystemConfig sysConf;
    private MailConfig mailConfig;
    private UcmdbDashConfig ucmdbDashConfig;
    private Map<ReferenceSystem, ReferenceSystemConfig> refSystemConfigs;
    private KVDbConfigs kvdbconfigs;
    private AmeiseConfig ameiseConfig;

    private States state;

    public ServerCache() {
        init();
    }

    @PostConstruct
    public void init() {
        state = States.BEFORESTARTED;
        long start = System.currentTimeMillis();
        
        try {
            mailConfig = new MailConfig();
            sysConf = new SystemConfig();
            ucmdbDashConfig = new UcmdbDashConfig();
MailConfig sieht folgendermaßen aus:
Java:
import javax.inject.Inject;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import main.java.persistence.entities.AtcParameter;
import main.java.persistence.interfaces.AtcParameterDbInterface;

public class MailConfig {

    private static final Logger LOG = LogManager.getLogger(MailConfig.class);
    
    private String server;
    private String sender;
    private Integer port;
    private Map<String, Object> otherMailConfig;

    @Inject
    private AtcParameterDbInterface dbInterface;

    public MailConfig() {
        List<AtcParameter> mailParameter = dbInterface.loadMailParameterTable();
        otherMailConfig = new HashMap<>();
Und AtcParameterDbInterface sieht so aus:
Java:
import javax.enterprise.context.Dependent;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import main.java.atc.definitions.ReferenceSystem;
import main.java.configuration.cache.kvdb.KVStage;
import main.java.persistence.entities.AtcParameter;

@Dependent
public class AtcParameterDbInterface {

    private final static Logger LOG = LogManager.getLogger(AtcParameterDbInterface.class);

    @PersistenceContext(unitName = "atcdb")
    private EntityManager em;

    public List<AtcParameter> loadActiveSystemParameter() {
        TypedQuery<AtcParameter> query = em.createQuery(StaticJPQL.ACTIVECACHEPARAMETER, AtcParameter.class);
        List<AtcParameter> resultList = query.getResultList();
        return resultList;
    }
Wenn ich das debugge, dann ist in der MailConfig in Zeile 25 das dbInterface null, obwohl es meiner Meinung nach dasselbe, wie tools am Anfang ist. Irgend etwas mache ich hier scheinbar falsch, dass das Inject nicht greift. Ich habe auch schon versucht statt @Dependent @stateful zu verwenden, kommt aber auf denselben Fehler.
 

Ähnliche Java Themen


Oben