1. 程式人生 > >Spring+Hibernate的連線池配置(c3p0,proxool)

Spring+Hibernate的連線池配置(c3p0,proxool)

本文所有的jar包都可以在我的資源裡下載

1、proxool

首先需要匯入jar包proxool-0.9.1.jar和proxool-cglib.jar;

建立proxool.xml,內容如下:

<?xml version="1.0" encoding="utf-8"?>
<something-else-entirely>
<proxool>
<alias>proxoolPool</alias>
<driver-url>jdbc:oracle:thin:@192.168.1.153:1521:ORCL</driver-url>
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
<driver-properties>
<property name="user" value="username" />
<property name="password" value="password" />
</driver-properties>
<house-keeping-sleep-time>90000</house-keeping-sleep-time>
<prototype-count>15</prototype-count>
<maximum-connection-count>20</maximum-connection-count>
<minimum-connection-count>5</minimum-connection-count>
<house-keeping-test-sql>select CURRENT_DATE</house-keeping-test-sql>
</proxool>
</something-else-entirely>

然後在web.xml中載入該xml檔案

......

<servlet>
  <servlet-name>proxoolInitialServlet</servlet-name>
  <servlet-class>
   org.logicalcobwebs.proxool.configuration.ServletConfigurator
  </servlet-class>
  <init-param>
   <param-name>xmlFile</param-name>
   <param-value>/

WEB-INF/classes/proxool.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet>
  <servlet-name>proxool</servlet-name>
  <servlet-class>
   org.logicalcobwebs.proxool.admin.servlet.AdminServlet
  </servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>proxool</servlet-name>
  <url-pattern>/Admin/proxool</url-pattern>
 </servlet-mapping>
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
   classpath:applicationContext.xml,
   classpath:applicationContext-*.xml
  </param-value>
 </context-param>
 <servlet>
  <servlet-name>contextConfigLocation</servlet-name>
  <servlet-class>
   org.springframework.web.context.ContextLoaderServlet
  </servlet-class>
  <load-on-startup>2</load-on-startup>
 </servlet>

......

接下來在spring中配置

<bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>org.logicalcobwebs.proxool.ProxoolDriver</value>
  </property>
  <property name="url">
   <value>proxool.proxoolPool</value>
  </property>
 </bean>

 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
  <property name="configLocation"
   value="classpath:hibernate.cfg.xml">
  </property>
 </bean>

在hibernate.cfg.xml中就不要配置連線源了,只是寫一些mapping就可以了

2、c3p0

首先匯入jar包c3p0-0.9.1.2.jar和c3p0-oracle-thin-extras-0.9.1.2.jar;

這個只需要配置spring的配置檔案就好,配置內容如下:

<bean id="dataSource"
  class="com.mchange.v2.c3p0.ComboPooledDataSource"
  destroy-method="close">
  <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property>
  <property name="jdbcUrl" value="jdbc:oracle:thin:@192.168.1.153:1521:ORCL"></property>
  <property name="user" value="username"></property>
  <property name="password" value="password"></property>
  <!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 1 -->
  <property name="acquireIncrement"
   value="1">
  </property>
  <!--初始化時獲取三個連線,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
  <property name="initialPoolSize"
   value="3">
  </property>
  <!--最大空閒時間,30秒內未使用則連線被丟棄。若為0則永不丟棄。Default: 0 -->
  <property name="maxIdleTime" value="30"></property>
  <!--連線池中保留的最大連線數。Default: 15 -->
  <property name="maxPoolSize" value="15"></property>
  <!--連線池中保留的最小連線數。Default: 3 -->
  <property name="minPoolSize" value="3"></property>
  <!--兩次連線中間隔時間,單位毫秒。Default: 1000 -->
  <property name="acquireRetryDelay" value="1000"></property>
  <!--定義在從資料庫獲取新連線失敗後重復嘗試的次數。Default: 30 -->
  <property name="acquireRetryAttempts" value="60"></property>
  <!--獲取連線失敗將會引起所有等待連線池來獲取連線的執行緒丟擲異常。但是資料來源仍有效
  保留,並在下次呼叫getConnection()的時候繼續嘗試獲取連線。如果設為true,那麼在嘗試
  獲取連線失敗後該資料來源將申明已斷開並永久關閉。Default: false-->
  
  <property name="breakAfterAcquireFailure" value="false"></property>
 </bean>
 <!--Hibernate SessionFatory-->
  <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
   <property name="configLocations">
   <list>
    <value>classpath:hibernate.cfg.xml</value>
   </list>
  </property>
  <property name="configurationClass"
   value="org.hibernate.cfg.AnnotationConfiguration" />
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">
     org.hibernate.dialect.Oracle9Dialect
    </prop>
    <prop key="hibernate.show_sql">
     false
    </prop>
    <prop key="hibernate.max_fetch_depth">
     3

    </prop>
    <!-- 配置C3P0ConnectionProvider-->
    <prop key="hibernate.connection.provider_class">
     org.hibernate.connection.C3P0ConnectionProvider
    </prop>
    </props>
  </property>
 </bean>

配置結束,完活

3、需要注意的事項:

。如果dao繼承HibernateDaoSupport,一定要用getHibernateTemplate(),不要使用getSession(),因為getSession()會得到原始的session,session的關閉以及事務完全不受spring控制了,我想既然是整合還是由spring來控制session會比較好,有些方法getHibernateTemplate()裡面沒有非要用session怎麼辦,方法如下:

public int queryByUserCount(final Users user) {
  log.debug("finding Users count instance by example");
  try {
   int result = (Integer) getHibernateTemplate().execute(
     new HibernateCallback() {
      public Object doInHibernate(Session arg0)
        throws HibernateException, SQLException {
       Example example = Example.create(user)
         .excludeZeroes().ignoreCase().enableLike(); // use
                    // like
                    // for
                    // string
                    // comparisons
       Criteria crit = arg0.createCriteria(Users.class)
         .add(example).addOrder(Order.asc("userid"));
       int totalPage = ((Number) crit.setProjection(
         Projections.rowCount()).uniqueResult())
         .intValue();

       return totalPage;
      }
     });
   return result;
  } catch (RuntimeException re) {
   log.error("find by example failed", re);
   throw re;
  }
 }

。如果在service類或者它的父類中定義了私有ApplicationContext的私有變數,千萬不要通過new的形式來獲得,否則spring在載入service類的時候會給每個ApplicationContext建立一個連線,也就是連線數和service的個數是一樣的,即使在變數前面加上static final,也會在啟動tomcat的時候建立一個連線,在啟動tomcat的時候,本來在web.xml中已經載入過spring的配置檔案,會建立一個連線,這樣就會有兩個連線,如果你的連線池初始連線數是3,就會建立3*2=6的連線數,如果變數前面沒加static final,啟動tomcat建立的連線數就是service類的個數*3+1,這樣很容易達到資料庫允許連線的上限導致無法獲得連線,這顯然不是我們想要的,解決方法就是一、避免在service中定義ApplicationContext的私有變數,這有點不合人情;二、從web.xml中獲取ApplicationContext,方法如下:

首先新建一個類SJBInit.java,內容如下:

public class SJBInit {
 /**
  * 系統應用spring環境
  */
 private static ApplicationContext ctx;

 /**
  * 單例項物件
  */
 private static SJBInit instance = null;

    Vector temp = new Vector(50);

    /**
  * 建構函式
  */
 public SJBInit() {
  if (instance == null){
   instance = this;
  }
 }

 /**
  * 獲得單例項物件
  *
  * @return
  */
 public static SJBInit getInstance() {
  if (instance == null)
   new SJBInit();
  return instance;
 }

 /**
  * 初始化Spring元件
  */
 public void init(Properties props) throws Exception {

  loadContextXML(props);

 }

 /**
  * 載入spring物件
  *
  * @param props
  */
 private void loadContextXML(Properties props) throws Exception{
  String path ="";
  /*LogFactory.getInstance().logRun(RunPriority.INFORMATIONAL,
    LogConstants.sysLogConstants.INT_SPRING_START,
    null
    );*/
  try {
   ServletContext servletContext = (ServletContext) props
     .get("APP_CONTEXT");
            if (servletContext != null)
    ctx = WebApplicationContextUtils
      .getRequiredWebApplicationContext(servletContext);
  }
  
  catch (Exception e) {
            e.printStackTrace();


        }
  if ((ctx == null) || (ctx.getBeanDefinitionNames().length == 0)) {

  }
  
 }

 /**
  * 得到spring的所有配置檔案
  *
  * @param path
  * @return
  */
 private void setConfigFiles(String path) {

  File file = new File(path);
  if (file.isDirectory()) {
   File[] files = file.listFiles();
   for (int index = 0; index < files.length; index++) {
    String filePath = files[index].getPath();
    if (filePath.endsWith(".xml")) {
     this.temp.add(filePath);
    }
   }
  }
 }

 /**
  * 得到一個spring的配置物件
  *
  * @param name
  * @return
  */
 public Object getBean(String name) {
  if (ctx == null)
   return null;
  else
   return ctx.getBean(name);
 }

 
 /**
  * 獲取單個資訊
  *
  * @param key
  * @param object
  * @param request
  * @return
  */
 public static String getMessage(String key, Object[] object, Locale locale) {
  return ctx.getMessage(key, object, locale);
 }

}

然後建立InitServlet.java,內容如下:

public class InitServlet extends HttpServlet{
 
 static final long serialVersionUID = -1111516993124229949L;
 
 /**
  * 啟動物件例項
  */
 private SJBInit sjbinit = SJBInit.getInstance();

 /**
  * servlet初始化
  */
 public void init(ServletConfig config) throws ServletException {
  
  super.init(config);
  Properties props = new Properties();
  props.put("APP_CONTEXT", config.getServletContext());
  // 檔案路徑
  String prefix = getServletContext().getRealPath("/");
 
  // web應用路徑
  props.put("APP_PATH", prefix);
  
  try {
   
  sjbinit.init(props);
  
  }catch(Exception e){
   
  }
 }
}

然後在web.xml中配置

...

<servlet>
  <description>System init when start</description>
  <display-name>InitServlet</display-name>
  <servlet-name>InitServlet</servlet-name>
  <servlet-class>com.fone.platform.core.InitServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
   classpath:applicationContext.xml,
   classpath:applicationContext-*.xml
  </param-value>
 </context-param>
 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>

.....

然後再建立類SJBUtil.java,內容如下:

public class SJBUtil {
 /**
  * sjb管理類例項
  */
 private static SJBInit sjb = SJBInit.getInstance();

   
 /**
  * 得到一個系統配置 bean
  *
  * @param name bean的配置名稱
  * @return 如果系統沒有載入返回 null
  */
 public static Object getBean(String name) {
  return sjb.getBean(name);
 }

}

然後在service類中通過SJBUtil.getBean("...");就可以獲得spring中的dao類了

public UsersDAO getUsersDAO() {
  return (UsersDAO) SJBUtil.getBean(SJBNameConstants.DAO_USERS_BEAN_NAME);
 }

這個問題結束。

。如果連線數太過,報出heap記憶體溢位的異常,可以進行如下配置:

如果啟動tomcat的時候出現heap溢位,需要修改tomcat目錄下bin目錄下的兩個檔案:
1、catalina.bat:在第一行中新增set JAVA_OPTS=-Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
2、catalina.sh:在第一行中新增JAVA_OPTS=-Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m

如果執行tomcat還有heap記憶體溢位,就在tomcat中配置jdk引數-Xms512m -Xmx512m

tomcat->config->Tomcat 5.x->JDK,新增jdk之後,在下面新增引數-Xms512m -Xmx512m