How to embed HSQLDB in file with Spring to a WebAp

2019-02-11 04:52发布

问题:

I have a webApp with Spring and it works correctly when I use HSQLDB in server mode, but in file mode, it only passes the unit test. This is my data source:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />

     <property name="url" value="jdbc:hsqldb:hsql://localhost/images" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>

I just change this line

   <property name="url" value="jdbc:hsqldb:hsql://localhost/images" />    
   ( -- Server mode)

for this

     <property name="url" value="jdbc:hsqldb:file:data/images" />      
     (-- In file)

And it just passes the unit test, and fails in the web appp.

I suppose that in file mode, when I run the webapp, HSQLDB doesn't find the file of the database.

I already tried to put the data of the database in the root of the webapp and inside the web-inf, and it doesn't work.

回答1:

Well if you put the database inside the jar like this:

<property name="url" value="jdbc:hsqldb:res:/data/images" />

You only can use it as read only, if you try to insert or modify the database if will fail.

One solution is to put a listener in the web.xml so when the application start it will ce initialize with the root path of the application web.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>maf</display-name>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>


     <!--This is to get root of the aplication-->

    <listener>
        <listener-class>
   org.atoms.HsqlDatabaseListener
        </listener-class>
    </listener>


       <!--Este listener se encarga de inicializar todo el contenedor de Spring y mantener una variable en el
  ServletContext que apunta a dicho contenedor -->

    <listener>
        <listener-class>
   org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>




</web-app>

The listener:

package org.atoms;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;


/**
 *
 * @author atomsfat
 */
public class HsqlDatabaseListener implements ServletContextListener {

    private ServletContext context = null;

    public void contextInitialized(ServletContextEvent event) {
        context = event.getServletContext();

        String prefix = event.getServletContext().getRealPath("/");

        System.out.println("database root " + prefix);
        com.atoms.HsqlDatabasePathResolver.getInstance(prefix);


    }

    public void contextDestroyed(ServletContextEvent event) {
        context = event.getServletContext();

    }

The other class:

package com.atoms;


/**
 *
 * @author atomsfat
 */
public class HsqlDatabasePathResolver {


    private static HsqlDatabasePathResolver instance ;
    private static String applicationPath = "";

    private HsqlDatabasePathResolver() {
    }

      /** Get Instance.
   */
    static public synchronized HsqlDatabasePathResolver getInstance(String applicationPath) {

        if (instance == null) {

            HsqlDatabasePathResolver.applicationPath =
                    HsqlDatabasePathResolver.normalizePath(applicationPath);
            instance = new HsqlDatabasePathResolver();



            System.out.println("Inizalizando path : " + HsqlDatabasePathResolver.applicationPath);

        }
        return instance;
    }

    public  String getApplicationPath() {
        return applicationPath;
    }



      public  String getUrlDatabase(String urlDatabase) {


         return HsqlDatabasePathResolver.replaceAll(urlDatabase,"{apppath}", applicationPath);
    }


      /**

         *
         * replace the "\" character by "/" and remove relative paths
         *
         * @param path
         * @return
         */
        public static String normalizePath(String path) {
            if (path == null) {
                return null;
            }
            String normalized = path;
            if (normalized.equals("/.")) {
                return "/";
            }
            if (normalized.indexOf('\\') >= 0) {
                normalized = normalized.replace('\\', '/');
            }
            if (!normalized.startsWith("/") && normalized.indexOf(':') < 0) {
                normalized = "/" + normalized;
            }
            do {
                int index = normalized.indexOf("//");
                if (index < 0) {
                    break;
                }
                normalized = normalized.substring(0, index) + normalized.substring(index + 1);
            } while (true);
            do {
                int index = normalized.indexOf("/./");
                if (index < 0) {
                    break;
                }
                normalized = normalized.substring(0, index) + normalized.substring(index + 2);
            } while (true);
            do {
                int index = normalized.indexOf("/../");
                if (index >= 0) {
                    if (index == 0) {
                        return null;
                    }
                    int index2 = normalized.lastIndexOf('/', index - 1);
                    normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
                } else {
                    return normalized;
                }
            } while (true);
        }




        public static String replaceAll(String str, String match, String replace) {
            if (match == null || match.length() == 0) {
                return str;
            }
            if (replace == null) {
                replace = "";
            }
            if(match.equals(replace))return str;
            StringBuffer ret=new StringBuffer();
            int i = str.indexOf(match);
            int y = 0;
            while (i >= 0)
            {
                //System.out.println("i:"+i+" y:"+y);
                ret.append(str.substring(y, i));
                ret.append(replace);
                //str = str.substring(y, i) + replace + str.substring(i + match.length());
                y = i + match.length();
                i = str.indexOf(match,y);
            }
            ret.append(str.substring(y));
            return ret.toString();
        }



}

And this the configuration that I use in spring:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
     xmlns:jee="http://www.springframework.org/schema/jee"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd
               http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">

     <!-- La definición del Datasource -->
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" lazy-init="true"
      destroy-method="close">
            <property name="driverClassName" value="org.hsqldb.jdbcDriver" />

          <property name="url">
               <ref bean="dataBaseUrl"/>
           </property>

            <property name="username" value="sa" />
            <property name="password" value="" />
        </bean>

     <!-- La definición del Factory de Session con Anotaciones -->
        <bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" lazy-init="false">
            <property name="dataSource" ref="dataSource" />
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">
         org.hibernate.dialect.HSQLDialect
                    </prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.format_sql">true</prop>
                </props>
            </property>
            <property name="mappingResources">
                <list>
                    <value>Atoms.hbm.xml</value>
                </list>

            </property>
        </bean>
     <!--HibernaTemplate-->
        <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
            <property name="sessionFactory">
                <ref bean="sessionFactory" />
            </property>
        </bean>
     <!-- Definición de los DAO`s -->

        <bean id="ipBlancaDao" class="org.atoms.impl.AtomsDaoHibernateImpl">
            <property name="hibernateTemplate" ref="hibernateTemplate" />
        </bean>


        <!--If your are not running in Web this will initialize with the directory from    the  process was started note that this classes is a singleton so if you are running in web the listener already have initialize the class with the path of the class-->


        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>


     <bean id="hsqlDatabasePathResolver" class="com.atoms.HsqlDatabasePathResolver" factory-method="getInstance" lazy-init="false">
            <constructor-arg>
                <value>${user.dir}</value>
            </constructor-arg>
        </bean>

   <!--This bean just replace {apppath} whit the absolute path-->

        <bean id="dataBaseUrl" class="java.lang.String" factory-bean="hsqlDatabasePathResolver" lazy-init="false"
                      factory-method="getUrlDatabase">
            <constructor-arg>
                <value>jdbc:hsqldb:mem:{apppath}/WEB-INF/data/maf</value>
            </constructor-arg>

        </bean>  
    </beans>

And yes this work but is a mess , I think that the solution is the listener, with it you can get the path of the appWeb. if somebody can make this simple, please post the answer.



回答2:

I found the answer here:

http://coding.derkeiler.com/Archive/Java/comp.lang.java.databases/2003-11/0096.html

I put de data inside in a jar on my project persistenceLayerWithData.jar.

Then I set this propety to the datasource:

<property name="url" value="jdbc:hsqldb:res:/data/images" />

And I added the jar to the web-inf/lib and make the war (all this with maven) and deploy in tomcat and it works, also I deploy it in websphere 7 and also works.

But when I run whit mvn jetty:run it doesn't I suppose that when running jetty whit maven it doesn't put the jar in the claspath.



回答3:

You should use an absolute file path. When you run your web application, your "current" (or "working") directory is most likely the bin folder of your application server. Therefore, when you specify relative path for HSQLDB URL it tries to load this file from there as opposed to somewhere relative to your webapp root.