1. 程式人生 > >整合之道--Spring4整合Hibernate5

整合之道--Spring4整合Hibernate5

Spring與Hiberante整合

通過Hibernate的學習,我們知道,hibernate主要在hibernate.cfg.xml配置檔案中
接下來我們看一下hibernate的一個配置檔案

hibernate配置檔案

hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration> <session-factory> <!-- 指定連線資料庫所用的驅動 --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <!-- 指定連線資料庫的url,其中hibernate是本應用連線的資料庫名 --> <property name="connection.url">jdbc:mysql://localhost/hibernate_test</property
>
<!-- 指定連線資料庫的使用者名稱 --> <property name="connection.username">root</property> <!-- 指定連線資料庫的密碼 --> <property name="connection.password">cheng</property> <!-- 指定連線池裡最大連線數 --> <property name="hibernate.c3p0.max_size"
>
20</property> <!-- 指定連線池裡最小連線數 --> <property name="hibernate.c3p0.min_size">1</property> <!-- 指定連線池裡連線的超時時長 --> <property name="hibernate.c3p0.timeout">5000</property> <!-- 指定連線池裡最大快取多少個Statement物件 --> <property name="hibernate.c3p0.max_statements">100</property> <property name="hibernate.c3p0.idle_test_period">3000</property> <property name="hibernate.c3p0.acquire_increment">2</property> <property name="hibernate.c3p0.validate">true</property> <!-- 指定資料庫方言 --> <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- 根據需要自動建立資料表 --> <property name="hbm2ddl.auto">update</property><!--①--> <!-- 顯示Hibernate持久化操作所生成的SQL --> <property name="show_sql">true</property> <!-- 將SQL指令碼進行格式化後再輸出 --> <property name="hibernate.format_sql">true</property> <!-- 避免這個錯誤資訊Disabling contextual LOB creation as createClob() method threw error :java.lang.reflect.InvocationTargetException --> <property name="hibernate.temp.use_jdbc_metadata_defaults">false</property> <!-- 羅列所有持久化類的類名 --> <mapping class="com.wechat.entity.po.User"/> <mapping class="com.wechat.entity.po.Person"/> </session-factory> </hibernate-configuration>

配置檔案的作用

hibernate.cfg.xml檔案的主要作用就是配置了一個session-factory

  1. session-factory中主要通過property配置一些資料庫的連線資訊,我們知道,Spring通常會將這種資料庫連線用dataSource來表示,這樣一來,hibernate.cfg.xml檔案中的所有跟資料庫連線的都可以幹掉了,直接用spring的dataSource,而dataSource也可以用c3p0、dbcp等。

  2. session-factory中通過property除了配置一些資料庫的連線資訊之外,還有一些hibernate的配置,比如方言、自動建立表機制、格式化sql等,這些資訊也需要配置起來。

  3. 還有最關鍵的一個持久化類所在路徑的配置

當不採用spring整合的時候,我們使用hibernate時主要是用hibernate從sessionFactory中去的session,然後用session來操作持久化物件,而sessionFactory來自於配置檔案。像下面這樣:

    StandardServiceRegistry registry =  null;
    SessionFactory sessionFactory = null;
    Session session = null;
    Transaction transaction = null;
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Before
    public void init() {

        registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
        session = sessionFactory.openSession();
        //開始事務
        transaction = session.getTransaction();
        transaction.begin();
    }

    @Test
    public void testSaveUser() {
        User user = new User();
        user.setUsername("張學友");
        user.setPassword("jacky");
        user.setRegistDate(sdf.format(new Date()));
        File file = new File("D:"+File.separator+"ubuntu.png");
        String fileName = file.getName();
        String prefix=fileName.substring(fileName.lastIndexOf(".")+1);
        System.out.println(prefix);
        InputStream input = null;
        try {
            input = new FileInputStream(file);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        Blob image = null;
        try {
            image = Hibernate.getLobCreator(session).createBlob(input,input.available());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        user.setUserPic(image);
        session.save(user);
    }

    @After 
    public void destroy(){
        transaction.commit();
        session.close();
        sessionFactory.close();
        StandardServiceRegistryBuilder.destroy( registry );
    }

Spring對hibernate的整合就是將上述三點通過spring配置起來,而hibernate最關鍵的sessionFactroy就是spring的一個bean

這些理解了整合就簡單了,

SessionFactoryBean

spring的sessionFactroy像下面這樣配置:

<!-- 載入配置檔案 -->
    <context:property-placeholder location="classpath:jdbc.properties"
        file-encoding="utf-8" ignore-unresolvable="true" />

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan">
            <list>
                <!-- 可以加多個包 -->
                <value>com.wechat.entity.po</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
            </props>
        </property>
    </bean>

通過bean的配置可以看出該bean就是hibernate的sessionFactroy
因為它指向了org.springframework.orm.hibernate5.LocalSessionFactoryBean

在這個bean中主要配置了上面說的三點:

  1. 資料來源dataSource
  2. hibernate的配置,包括方言,輸出sql等
  3. 持久化類的位置,通過包進行掃描

下面給出資料來源dataSource的配置

dataSource

<!-- 配置資料來源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close" p:driverClass="${jdbc.driverClassName}"
        p:jdbcUrl="${jdbc.url}" p:user="${jdbc.username}" p:password="${jdbc.password}"
        p:testConnectionOnCheckout="${jdbc.c3p0.testConnectionOnCheckout}"
        p:testConnectionOnCheckin="${jdbc.c3p0.testConnectionOnCheckin}"
        p:idleConnectionTestPeriod="${jdbc.c3p0.idleConnectionTestPeriod}"
        p:initialPoolSize="${jdbc.c3p0.initialPoolSize}" p:minPoolSize="${jdbc.c3p0.minPoolSize}"
        p:maxPoolSize="${jdbc.c3p0.maxPoolSize}" p:maxIdleTime="${jdbc.c3p0.maxIdleTime}" />

還有資料庫的連線資訊

jdbc.properties

#-----------------------------------------------------
# 資料庫配置
#-----------------------------------------------------
#伺服器地址
host=127.0.0.1
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://${host}:3306/hibernate_test
jdbc.username=root
jdbc.password=cheng

#-----------------------------------------------------
# 適用於c3p0的配置
#-----------------------------------------------------
#-----------------------------------------------------
# c3p0反空閒設定,防止8小時失效問題28800
#-----------------------------------------------------
#idleConnectionTestPeriod要小於MySQL的wait_timeout
jdbc.c3p0.testConnectionOnCheckout=false
jdbc.c3p0.testConnectionOnCheckin=true
jdbc.c3p0.idleConnectionTestPeriod=3600
#-----------------------------------------------------
# c3p0連線池配置
#-----------------------------------------------------
#initialPoolSize, minPoolSize, maxPoolSize define the number of Connections that will be pooled.
#Please ensure that minPoolSize <= maxPoolSize.
#Unreasonable values of initialPoolSize will be ignored, and minPoolSize will be used instead.
jdbc.c3p0.initialPoolSize=10
jdbc.c3p0.minPoolSize=10
jdbc.c3p0.maxPoolSize=100
#maxIdleTime defines how many seconds a Connection should be permitted to go unused before being culled from the pool.
jdbc.c3p0.maxIdleTime=3600
#-----------------------------------------------------
# hibernate連線池配置
#-----------------------------------------------------
hibernate.connection.driverClass=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://${host}:3306/${dbName}
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.hbm2ddl.auto=update

配置完這些還有spring強大的事務管理

<!-- 配置Hibernate事務管理器 -->
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- 配置事務異常封裝 -->
    <bean id="persistenceExceptionTranslationPostProcessor"
        class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

    <!-- 基於資料來源的事務管理器 -->
    <!-- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" 
        p:dataSource-ref="dataSource" /> -->

    <!-- 配合<tx:advice>和<aop:advisor>完成了事務切面的定義 -->
    <!-- 使用強大的切點表示式是語言輕鬆定義目標方法 -->
    <aop:config proxy-target-class="true">
        <!-- 通過aop定義事務增強切面 -->
        <aop:pointcut expression=" execution(* com.wechat.service..*(..))"
            id="serviceMethod" />
        <!-- 引用事務增強 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
    </aop:config>
    <!-- 事務增強 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 事務屬性定義 -->
        <tx:attributes>
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

好了,這些配置好之後就可以使用在spring中配置的sessionFactroy了

UserDao

package com.wechat.dao;

import java.util.List;

import com.wechat.entity.po.User;

public interface UserDao {
    // 得到所有使用者
    public List<User> getAllUser();

    // 檢測使用者名稱是否存在
    public boolean isExists(String username);

}

實現類

package com.wechat.dao.impl;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.wechat.dao.UserDao;
import com.wechat.entity.po.User;
@Repository
public class UserDaoImpl implements UserDao {
    //注入sessionFactory
    @Autowired
    private SessionFactory sessionFactory;

    @SuppressWarnings("unchecked")
    @Override
    public List<User> getAllUser() {
        List<User> userList = new ArrayList<User>();
        String hsql="from User";
        Session session = sessionFactory.getCurrentSession();
        Query query = session.createQuery(hsql);
        userList = query.list();
        return userList;
    }

    @Override
    public boolean isExists(String username) {
        Query query = sessionFactory.openSession()
                .createQuery("from User u where u.username = :username").setParameter("username", username);
        System.out.println(query.list().size());
        return query.list().size()>0?true:false;
    }

}

UserService

package com.wechat.service.user;

import java.util.List;

import com.wechat.entity.po.User;

public interface UserService {
    public List<User> getAllUser();
    public boolean isExists(String username);

}

實現類

package com.wechat.service.user.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.wechat.dao.UserDao;
import com.wechat.entity.po.User;
import com.wechat.service.user.UserService;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public List<User> getAllUser() {
        return userDao.getAllUser();
    }
    @Override
    @Cacheable(cacheNames="isExists", key="#username")
    public boolean isExists(String username) {
        return userDao.isExists(username);
    }

}

因為事務管理是配置在service層,所以用service來測試

測試

package com.wechat.dao;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.wechat.entity.po.User;
import com.wechat.service.user.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/spring-core.xml" })
public class UserServiceTest {
    @Autowired
    private UserService userService;

    @Test
    public void test() {
        List<User> userList = userService.getAllUser();
        for(User user:userList){
            System.out.println(user.getUsername());
        }

    }

}

輸入結果

Hibernate: 
    select
        user0_.userid as userid1_2_,
        user0_.password as password2_2_,
        user0_.registDate as registDa3_2_,
        user0_.userPic as userPic4_2_,
        user0_.username as username5_2_ 
    from
        user_info user0_
程高偉
張學友

資料庫表

這裡寫圖片描述

好了Spring整合hibernate就寫到這裡。

專案地址