Należy ustawić właściwości połączenia z bazą danych w pliku server.xml lub context.xml

79

Próbuję skonfigurować właściwości połączenia z bazą danych przy użyciu JNDI dla aplikacji internetowej Spring.

Rozważam dwa podejścia, jak poniżej:

Podejście 1:

W swojej konfiguracji Spring możesz mieć coś takiego:

<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/facs"/>

Następnie w pliku /META-INF/context.xml swojej aplikacji internetowej powinieneś mieć również coś podobnego:

<?xml version='1.0' encoding='utf-8'?>

<!-- antiResourceLocking="true" -->
<Context path="/podd-apn"
         reloadable="true"
         cachingAllowed="false"
         antiResourceLocking="true"
         >

  <Resource name="jdbc/facs"              
            type="javax.sql.DataSource" username="${database.username}" password="${database.password}"
            driverClassName="org.postgresql.Driver" 
            url="${database.url}"
            maxActive="8" maxIdle="4"
            global="jdbc/facs" 
            />


</Context>

A w swoim web.xml powinieneś coś takiego:

<!-- JNDI -->
  <resource-ref>
    <description>FACs Datasource</description>
    <res-ref-name>jdbc/facs</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref> 


Podejście 2:

Konfiguracja w kontekście Spring w następujący sposób:

<jee:jndi-lookup id="dbDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

Możesz zadeklarować zasób JNDI w pliku server.xml Tomcata, używając czegoś takiego:

<GlobalNamingResources>
  <Resource name="jdbc/DatabaseName" auth="Container" type="javax.sql.DataSource"
              username="dbUsername" password="dbPasswd"
              url="jdbc:postgresql://localhost/dbname"
              driverClassName="org.postgresql.Driver"
              initialSize="5" maxWait="5000"
              maxActive="120" maxIdle="5"
              validationQuery="select 1"
              poolPreparedStatements="true"/>
</GlobalNamingResources/>

I odwołaj się do zasobu JNDI z pliku kontekstowego.xml serwera Tomcat w następujący sposób:

<ResourceLink name="jdbc/DatabaseName"
   global="jdbc/DatabaseName"
   type="javax.sql.DataSource"/>


Moje pytanie brzmi, gdzie najlepiej przechowywać właściwości bazy danych? Czy należy je umieścić w pliku server.xml czy context.xml ?

Jeśli mam 2 bazy danych, czy powinienem użyć dwóch konfiguracji?

Czy najlepiej jest umieszczać je bezpośrednio w pliku server.xml lub context.xml? Czy muszę konfigurować za pomocą konsoli GUI Tomcat Manager?

Dzięki!

user1016403
źródło

Odpowiedzi:

27

Wolę trzecie podejście, które czerpie to, co najlepsze z Podejścia 1 i Podejścia 2 opisanego przez użytkownika1016403 .

Podejście 3

  1. Zapisz właściwości bazy danych w server.xml
  2. odwołać się do server.xmlwłaściwości bazy danych z aplikacji internetowejMETA-INF/context.xml

Podejście 3 korzyści

Podczas gdy pierwszy punkt jest przydatny ze względów bezpieczeństwa, drugi punkt jest przydatny do odwoływania się do wartości właściwości serwera z aplikacji internetowej, nawet jeśli wartości właściwości serwera ulegną zmianie.

Ponadto oddzielenie definicji zasobów na serwerze od ich wykorzystania przez aplikację internetową sprawia, że ​​taka konfiguracja jest skalowalna w różnych organizacjach o różnym stopniu złożoności, w których różne zespoły pracują na różnych poziomach / warstwach: zespół administratorów serwera może pracować bez konfliktu z zespołem programistów, jeśli administrator udostępnia to samo Nazwa JNDI z deweloperem dla każdego zasobu.

Podejście 3 wdrożenie

Zdefiniuj nazwę JNDI jdbc/ApplicationContext_DatabaseName.

Zadeklaruj jdbc/ApplicationContext_DatabaseNameróżne właściwości i wartości w Tomcat, server.xmlużywając czegoś takiego:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContext_DatabaseName" auth="Container" type="javax.sql.DataSource"
              username="dbUsername" password="dbPasswd"
              url="jdbc:postgresql://localhost/dbname"
              driverClassName="org.postgresql.Driver"
              initialSize="5" maxWait="5000"
              maxActive="120" maxIdle="5"
              validationQuery="select 1"
              poolPreparedStatements="true"/>
</GlobalNamingResources/>

Połącz jdbc/ApplicationContext_DatabaseNamewłaściwości z aplikacji internetowej META-INF/context.xmlza pomocą prywatnego kontekstu JNDI aplikacji java:comp/env/określonego w nameatrybucie:

<Context path="/ApplicationContext" ... >
  <!--
    "global" attribute links to GlobalNamingResources in the ${catalina.base}/conf/server.xml (server administrator team)
    "name" attribute is relative to the application-private JNDI context java:comp/env/ and is looked up from the java web application (application developer team)
  -->
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName" name="jdbc/DatabaseName" type="javax.sql.DataSource"/>
</Context>

Na koniec, aby użyć zasobu JNDI, określ nazwę JNDI jdbc/DatabaseNamew deskryptorze wdrażania aplikacji WWW:

<resource-ref>
    <description>DatabaseName's Datasource</description>
    <res-ref-name>jdbc/DatabaseName</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref> 

aw kontekście wiosny:

<jee:jndi-lookup id="DatabaseNameDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

Podejmij 3 wady

Jeśli nazwa JNDI zostanie zmieniona wtedy zarówno server.xmli META-INF/context.xmlbędą musiały być edytowane i wdrożyć byłoby konieczne; niemniej jednak ten scenariusz jest rzadki.

Podejdź do 3 odmian

Wiele źródeł danych używanych przez jedną aplikację internetową

Po prostu dodaj konfiguracje do Tomcat server.xml:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContext_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContext_DatabaseName2" ... />
  ...
</GlobalNamingResources/>

Dodaj aplikację internetową odsyłacza META-INF/context.xmlza pomocą prywatnego kontekstu JNDI aplikacji java:comp/env/określonego w nameatrybucie:

<Context path="/ApplicationContext" ... >
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName1" name="jdbc/DatabaseName1" ... />
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName2" name="jdbc/DatabaseName2" ... />
  ...
</Context>

Na koniec dodaj użycie zasobów JNDI w deskryptorze wdrażania aplikacji internetowej:

<resource-ref>
    <description>DatabaseName1's Datasource</description>
    <res-ref-name>jdbc/DatabaseName1</res-ref-name> ... 
</resource-ref> 
<resource-ref>
    <description>DatabaseName2's Datasource</description>
    <res-ref-name>jdbc/DatabaseName2</res-ref-name> ... 
</resource-ref>
...

aw kontekście wiosny:

<jee:jndi-lookup id="DatabaseName1DataSource"
   jndi-name="jdbc/DatabaseName1" ... />
<jee:jndi-lookup id="DatabaseName2DataSource"
   jndi-name="jdbc/DatabaseName2" ... />
...


Wiele źródeł danych używanych przez wiele aplikacji internetowych na tym samym serwerze

Po prostu dodaj konfigurację do Tomcat server.xml:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContextX_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContextX_DatabaseName2" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName2" ... />
  ...
</GlobalNamingResources/>

inne konfiguracje powinny dać się wyprowadzić z poprzedniego przypadku zmienności.


Wiele źródeł danych do tej samej bazy danych używanej przez wiele aplikacji internetowych na tym samym serwerze

W takim przypadku server.xmlkonfiguracje Tomcata, takie jak:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContextX_DatabaseName" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName" ... />

kończy się w dwóch różnych aplikacjach internetowych, META-INF/context.xml takich jak:

<Context path="/ApplicationContextX" ... >
  <ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>

i jak:

<Context path="/ApplicationContextY" ... >
  <ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>

więc ktoś może się martwić faktem, że to samo name="jdbc/DatabaseName"jest wyszukiwane, a następnie używane przez dwie różne aplikacje wdrożone na tym samym serwerze: nie stanowi to problemu, ponieważ jdbc/DatabaseNamejest to kontekst JNDI prywatnej aplikacji java:comp/env/, więc ApplicationContextX używającjava:comp/env/ nie można (zgodnie z projektem) wyszukuje zasoby, z którymi jest powiązany global="jdbc/ApplicationContextY_DatabaseName".

Oczywiście, jeśli czułeś się bardziej zrelaksowany bez tego zmartwienia, możesz użyć innej strategii nazewnictwa, na przykład:

<Context path="/ApplicationContextX" ... >
  <ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/applicationXprivateDatabaseName" ... />
</Context>

i jak:

<Context path="/ApplicationContextY" ... >
  <ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/applicationYprivateDatabaseName" ... />
</Context>
taringamberini
źródło
Pytanie dotyczące scenariusza „Wiele źródeł danych do tej samej bazy danych używanych przez wiele aplikacji internetowych na tym samym serwerze”… <Resource name="jdbc/ApplicationContextX_DatabaseName" ... /> <Resource name="jdbc/ApplicationContextY_DatabaseName" ... /> Gdyby zasoby były pulami połączeń, czy dałoby to dwie oddzielne pule, po jednej na aplikację internetową? Zważywszy, że jeśli połączyłem się z obu aplikacji internetowych do jednego zasobu, byłaby tylko jedna pula połączeń, prawda? Jakieś powody, by preferować jeden od drugiego? (oddzielne pule połączeń DB, po jednej na aplikację internetową w porównaniu z jedną pulą połączeń współdzieloną przez wszystkie aplikacje internetowe)? Dzięki.
Rebeccah
1
@Rebeccah - P1: Gdyby zasoby były pulami połączeń, czy dałoby to dwie oddzielne pule, po jednej na aplikację internetową? A1: Tak, tak.
taringamberini
1
@Rebeccah - Q2: Natomiast gdybym połączył się z obu aplikacji internetowych do jednego zasobu, byłaby tylko jedna pula połączeń, prawda? A2: Dobrze.
taringamberini
2
@Rebeccah - P3: Czy są jakieś powody, by preferować jedno od drugiego? (oddzielne pule połączeń DB, po jednej na aplikację internetową w porównaniu z jedną pulą połączeń współdzieloną przez wszystkie aplikacje internetowe)? O3: Wolę „oddzielne pule połączeń DB, po jednym na aplikację internetową”, ponieważ nie chciałbym, aby intensywne korzystanie z webAppX powodowało spowolnienie webAppY z powodu wyczerpania puli połączeń webAppX; ponadto system monitorowania i logowania bazy danych nie był w stanie rozróżnić żądań webAppX i webAppY, więc zrozumienie i wyizolowanie problemów będzie trudne lub co gorsza niemożliwe.
taringamberini
23

YOUR_APP.xml plik

Preferuję podejście 2 (umieść wszystko (nie tylko jakiś atrybut w konfiguracji), ale zamiast umieszczać je w globalnym server.xmllub globalnym context.xml, powinieneś umieścić je w pliku specyficznym dla aplikacji w swoim Tomcacie.context.xml.default YOUR_APP.xml

YOUR_APP.xmlPlik znajduje się $catalinaHome/conf/<engine>/<host>(na przykład conf/Catalina/localhost/YOUR_APP.xml).

Konfiguracja specyficzna dla aplikacji YOUR_APP.xmljest dostępna tylko dla określonej aplikacji.

Zobacz przewodnik opublikowany przez MuleSoft. Zobacz też oficjalną dokumentację Tomcat Configuration Reference , strona The Context Container

Cytując tę ​​dokumentację:

Poszczególne elementy kontekstu można jawnie zdefiniować:

•…

• W pojedynczych plikach (z rozszerzeniem „.xml”) w $CATALINA_BASE/conf/[enginename]/[hostname]/katalogu. Ścieżka kontekstowa i wersja zostaną pobrane z podstawowej nazwy pliku (nazwa pliku bez rozszerzenia .xml).

•…

Ralph
źródło
2
Dzięki za twoją odpowiedź. jeśli umieszczę wszystkie właściwości w naszych aplikacjach META-INF / context.xml? czy to najlepsze miejsce do zatrzymania?
user1016403
5
Nie sądzę, aby umieszczenie wartości niektórych właściwości INSIDE (na przykład w META-INF / context.xml) aplikacji było dobrym podejściem, ponieważ wtedy musisz ponownie skompilować i wdrożyć aplikację, jeśli właściwości się zmienią. - Więc to będzie prawie taka sama jak przy użyciu żadnych właściwości w ogóle i wprowadzania wartości bezpośrednio w config.xml wiosny
Ralph
Więc jakie jest zalecane miejsce do ich przechowywania?
user1016403
2
Dodałbym tylko, że zamiast definiować je dla wszystkich aplikacji przy użyciu context.xml.default, możesz użyć plików konfiguracji kontekstu specyficznych dla aplikacji, takich jak yourapp.xml w tym samym folderze, co pozwala wskazać dwa wdrożenia tej samej wojny różne bazy danych.
użyj 4ce
1
Niezbyt pomocna odpowiedź. Po prostu określa preferencje i nie podaje uzasadnienia.
DavidS
10

Podejście 4

Zamiast korzystać z JNDI, pracuję z .propertiesplikami i buduję złożone obiekty podczas inicjalizacji programu zamiast w czasie konfiguracji.

Używasz już Springa i jest to łatwe do skonstruowania DataSourcepoprzez:

<context:property-placeholder location="classpath:app.properties"/>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
    <property name="url" value="jdbc:oracle:thin:@${db.host}:${db.port}:${db.user}"/>
    <property name="username" value="${db.user}"/>
    <property name="password" value="${db.pass}"/>
</bean>

Całkowicie zgadzam się z Ralphem co do używania deskryptora wdrażania w, $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xmlale zamiast JNDI lubię zwykły plik klucz-wartość!

Dzięki wiosennemu wstrzykiwaniu powyższych właściwości na pola fasoli jest to łatwe:

@Value("${db.user}") String defaultSchema;

zamiast JNDI:

@Inject ApplicationContext context;
Enviroment env = context.getEnvironment();
String defaultSchema = env.getProperty("db.user");

Zauważ również, że EL pozwala na to (wartości domyślne i głębokie podstawianie rekurencyjne):

@Value('${db.user:testdb}') private String dbUserName;

<property name='username' value='${db.user.${env}}'/>

Do eksternalizacji .propertiespliku używam nowoczesnego Tomcat 7, który ma org.apache.catalina.loader.VirtualWebappLoader :

<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
        virtualClasspath="/srv/web/app/"/>

Więc twoi devopsy wypełniają virtualClasspathlokalne zewnętrzne pełne ścieżki, które są oddzielne dla aplikacji i umieszczają lokalnie app.propertiesw tym katalogu .

Zobacz też:

gavenkoa
źródło
0

Możesz również użyć obsługi adresów URL JNDI do różnych konfiguracji aplikacji na potrzeby testów, testów integracji i produkcji.

<Context>
...
<Resource auth="Container" factory="com.benasmussen.jndi.url.URLFactory" 
name="url/MyUrl" type="java.net.URL" url="file:///your/path/to/file"/>
...
</Context>

<jee:jndi-lookup id="myUrl" jndi-name="java:comp/env/url/MyUrl" expected-type="java.net.URL" />

Zapoznaj się z projektem GitHub Obsługa adresów URL JNDI Tomcat, aby włączyć obsługę adresów URL JNDI dla serwerów Tomcat.

Ben Asmussen
źródło
0

krok 1: context.xml

    <Context path="/projectname">
  <Resource auth="Container" 
            driverClassName="com.mysql.jdbc.Driver"
            logAbandoned="true" 
            maxActive="100" ``
            maxIdle="30" 
            maxWait="10000" 
            name="refname" 
            removeAbandoned="true" 
            removeAbandonedTimeout="60" 
            type="javax.sql.DataSource" 
            url="jdbc:mysql://localhost:8080/dbname" 
            username="root"
            password="root"/>
</Context>

Krok 2: web.xml

<resource-ref>
        <description>DB Connection</description>
        <res-ref-name>refname</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

Krok 3: Utwórz klasę, aby uzyskać połączenie

Connection connection = null;        
            Context context = (Context) new InitialContext().lookup("java:comp/env");
            DataSource ds = (DataSource) context.lookup("refname");
            connection = ds.getConnection();

Wszystko gotowe

tamil arasan
źródło