關於項目中的DAL數據接入層架構設計


摘要:項目中對關系型數據庫的接入再尋常不過,也有海量的ORM工具可供選擇,一個一般性的DAL數據接入層的結構卻大同小異,這里就分享一下使用Hibernate、Spring、Hessian這三大工具對DAL層的具體實現方法,也是對之前使用的一個總結
關鍵詞:Hibernate, Spring, Hessian, DAL, 數據接入層, 架構設計

注意:以下 配置 代碼 運行在Hibernate4.2.5,Spring3.2.4,Hessian4.0.37, Tomcat7.0.47 環境下
 
一、Model們
做數據接入,最根本的就是數據了,數據庫中的數據和JAVA中的對象完美映射是ORM-Hibernate做的事。 一般 項目中是先定義Model,再通過Model在數據庫中生成表結構,也有動態生成表結構、對象的需求,關於這點將在以后的博客中專門講述。下面的代碼段是比較普遍的基礎的“角色類”的Model定義,我們使用Spring通過標簽的形式注入,注意Entity和 Id標簽。我們一般還可以通過 @Column (name = "tableName", length = 50, nullable = false, unique = true) 這一標簽定義某一屬性在數據庫中映射字段的名稱、長度等信息,時間將通過 Calendar 這一JAVA類型映射,關於Model的定義還有很多其它理論,比如一對多、多對一、多對多的關系、延遲加載等,這里還是交給Hibernate相關書籍吧。
/**
 * 角色對象
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2013年7月30日上午10:32:55
 */
@SuppressWarnings("serial")
@Entity
public class Role implements Serializable
{
    private Long id;
    private String name;
    private String description;
 
    // ********************** Accessor Methods ********************** //
    @Id
    @GeneratedValue
    public Long getId()
    {
        return id;
    }
 
    public void setId(Long id)
    {
        this.id = id;
    }
 
    public String getName()
    {
        return name;
    }
 
    public void setName(String name)
    {
        this.name = name;
    }
 
    public String getDescription()
    {
        return description;
    }
 
    public void setDescription(String description)
    {
        this.description = description;
    }
}
 
別忘了寫完Model要在hibernate.cfg.xml里把它給配置上,要不然打死你也不能在數據庫中找到Role這張表的。hibernate.cfg.xml配置文件,配置了連接數據庫、如何使用Hibernate的配置信息,如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.username">username</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.connection.url">jdbc:mysql://IP地址:3306/schema名</property>        
        <!-- <property name="hibernate.connection.characterEncoding">gbk</property> -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.c3p0.max_size">1</property>
        <property name="hibernate.c3p0.min_size">1</property>
        <property name="hibernate.c3p0.timeout">180</property>
        <property name="hibernate.c3p0.max_statements">50</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hibernate.jdbc.fetch_size">50</property>
        <property name="hibernate.jdbc.batch_size">50</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- <property name="hibernate.connection.autocommit">true</property> -->
        <mapping class="com.wang.anqi.model.Role" />         
    </session-factory>
</hibernate-configuration>
 
二、DAO層
先定義RoleDAO這一接口,RoleDAO比較簡單,它在GenericDAO的基礎上增添了它獨有的接口:findByRoleName,按照角色名稱查找對應的用戶列表。 GenericDAO是各個DAO的通用模板,它包含了各基本數據訪問操作,如增刪改查CRUD等,繼承自它的類必然會支持這些操作。
/**
 * DAO層接口-角色
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2013年7月30日下午6:13:29
 */
public interface RoleDAO extends GenericDAO<Role, Long>
{
    /**
     * 按照角色名稱查找其對應的用戶列表
     * 
     * @param roleNmae 角色名稱
     * @return 用戶列表 List<User>
     * @throws DatabaseException 異常
     */
    List<User> findByRoleName(String roleName) throws DatabaseException;
}
/**
 * 各個DAO的通用模板接口。
 * <p>
 * 增刪改查(create, read, update, delete)這幾種基本數據訪問操作放在這個接口中。
 * <p>
 * @author wanganqi
 * @version v1.0
 * 
 * @since 2012年12月1日下午2:49:50
 * @param <T> 模板類型
 * @param <ID> 主鍵
 */
 
public interface GenericDAO<T, ID extends Serializable>
{
    /**
     * 添加
     * 
     * @param entity 實體類
     * @return 實體類
     */
    T saveOrUpdate(T entity);
 
    /**
     * 根據給定的id,以lock規定的鎖模式,查找並返回對應的實體對象。
     * 
     * @param id 主鍵
     * @param lock 是否加鎖
     * @return T 查找到的對象
     */
    T findById(ID id, boolean lock);
 
    /**
     * 按條件查找
     * 
     * @param criterion 條件
     * @return 實體對象列表
     */
    List<T> findByCriteria(Criterion... criterion);
 
    /**
     * 返回T類型的所有對象。
     * 
     * @return List<T> T對象的列表
     */
    List<T> findAll();
 
    /**
     * 查詢
     * 
     * @param exampleInstance 條件
     * @param excludeProperty 條件
     * @return 實體對象列表
     */
    List<T> findByExample(T exampleInstance, String... excludeProperty);
 
    /**
     * 將實體entity持久化。(添加或更新)
     * 
     * @param entity 需持久化的實體
     * @return 持久化對象
     */
    T makePersistent(T entity);
 
    /**
     * 將持久化實體entity從數據庫中刪除。
     * 
     * @param entity 需刪除的實體。
     */
    void makeTransient(T entity);
 
    /**
     * 根據ID刪除實體
     * 
     * @param id ID
     */
    void delete(ID id);
 
    /**
     * 同步對象在內存與數據庫中的狀態。
     */
    void flush();
 
    /**
     * 徹底清理當前session。
     */
    void clear();
 
    /**
     * 統一拋出數據庫操作異常
     * 
     * @param e 異常
     * @throws DatabaseException 數據庫異常
     */
    void throwDatabaseException(Exception e) throws DatabaseException;
 
    /**
     * 執行數據表結構更新
     * 
     * @return 是否更新成功
     * @throws DatabaseException 數據庫異常
     */
    boolean excuteUpdateShema() throws DatabaseException;
}
 
在DAO層還需要實現 RoleDAO接口,在這里只有一個實現,其它的功勞都要記在GenericDAOImpl身上。 GenericDAOImpl是對 GenericDAO的具體實現, GenericDAOImpl是各個DAOImpl的通用模板,它包含了各基本數據訪問操作,如增刪改查CRUD,其它 DAOImpl通過繼承 GenericDAOImpl來獲得CRUD的功能。注意,我們使用Spring實例化、注入 各個DAOImpl和SessionFactory,由於Hessian支持Spring方式發布,故Spring配置文件這里放在Hessian層,當然也可以放在BLL層,放在BLL層需要自己控制Spring上下文。
/**
 * DAO層接口實現-角色訪問
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2013年7月30日下午6:18:41
 */
@Repository("RoleDAOImpl")
public class RoleDAOImpl extends GenericDAOImpl<Role, Long> implements RoleDAO
{
    @SuppressWarnings("unchecked")
    @Override
    public List<User> findByRoleName(String roleName) throws DatabaseException
    {
        return getSession().createCriteria(User.class)
            .add(Restrictions.eq("roleName" roleName )).list();
    }
}
/**
 * You have to inject a current Hibernate <tt>Session</tt> to use a DAO.
 * Otherwise, this generic implementation will use
 * <tt>HibernateUtil.getSessionFactory()</tt> to obtain the curren
 * <tt>Session</tt>.
 
 * @author wanganqi
 * @version v1.0
 * @since 2012年8月1日下午2:46:08
 * @param <T> 模板類
 * @param <ID> 模板ID
 * 
 */
public abstract class GenericDAOImpl<T, ID extends Serializable> implements GenericDAO<T, ID>
{
    private Class<T> m_persistentClass;
 
    @Autowired
    private SessionFactory m_sessionFactory;
 
    @Autowired
    private LocalSessionFactoryBean m_sessionFactoryBean;
 
    /**
     * 構造函數
     * 
     */
 
    @SuppressWarnings("unchecked")
    public GenericDAOImpl()
    {
 
        // 運用反射機制,獲取當前具體的類的類型。
        this.m_persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
 
    /**
     * 獲取sessionFactory
     * 
     * @return SessionFactory
     */
    public SessionFactory getSessionFactory()
    {
        return m_sessionFactory;
    }
 
    protected Session getSession()
    {
           return   m_sessionFactory .getCurrentSession();
    }
 
    /**
     * 獲取模板類
     * 
     * @return 模板類
     */
    public Class<T> getPersistentClass()
    {
        return m_persistentClass;
    }
    @SuppressWarnings("unchecked")
    @Override
    public T findById(ID id, boolean lock)
    {
        T entity;
        if (lock)
        {
            entity = (T) getSession().get(getPersistentClass(), id, LockOptions.UPGRADE);
        }
        else
        {
            entity = (T) getSession().get(getPersistentClass(), id);
        }
        return entity;
    }
    @Override
    public List<T> findAll()
    {
        List<T> result = findByCriteria();
        return result;
    }
    @SuppressWarnings("unchecked")
    @Override
    public List<T> findByExample(T exampleInstance, String... excludeProperty)
    {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        Example example = Example.create(exampleInstance);
        for (String exclude : excludeProperty)
        {
            example.excludeProperty(exclude);
        }
        crit.add(example);
        return crit.list();
    }
    @SuppressWarnings("unchecked")
    @Override
    public T makePersistent(T entity)
    {
         return  (T) getSession().merge(entity);
 
    }
    @Override
    public void makeTransient(T entity)
    {
 
        getSession().delete(entity);
 
    }
 
    @SuppressWarnings("unchecked")
    @Override
    public void delete(ID id)
    {
        T entity = (T) getSession().get(getPersistentClass(), id);
        if (entity != null)
        {
            getSession().delete(entity);
        }
    }
    @Override
    public void flush()
    {
        getSession().flush();
    }
 
    @Override
    public void clear()
    {
        getSession().clear();
    }
 
    @SuppressWarnings("unchecked")
    @Override
    public List<T> findByCriteria(Criterion... criterion)
    {
        Criteria crit = getSession().createCriteria(getPersistentClass());
 
        for (Criterion c : criterion)
        {
            crit.add(c);
        }
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return crit.list();
    }
    @SuppressWarnings("unchecked")
    protected T queryForObject(String hql, Object[] params)
    {
        Query query = getSession().createQuery(hql);
        setQueryParams(query, params);
        return (T) query.uniqueResult();
    }
 
    private void setQueryParams(Query query, Object[] params)
    {
        if (null == params)
        {
            return;
        }
        for (int i = 0; i < params.length; i++)
        {
            query.setParameter(i, params[i]);
        }
    }
 
    @Override
    public T saveOrUpdate(T entity)
    {
        getSession().saveOrUpdate(entity);
        return entity;
    }
 
    @Override
    public void throwDatabaseException(Exception e) throws DatabaseException
    {
        DatabaseException de = new DatabaseException(new ErrMsg("數據庫操作異常", (e.getCause() != null ? e.getCause().getMessage()
            : e.getMessage())));
        throw de;
    }
 
    private FilenameFilter filter(final String extension)
    {
        return new FilenameFilter()
        {
            @Override
            public boolean accept(File file, String path)
            {
                String filename = new File(path).getName();
                return filename.indexOf(extension) != -1;
            }
        };
    }
 
    @Override
    public boolean excuteUpdateShema() throws DatabaseException
    {
        try
        {
            org.hibernate.cfg.Configuration config = new Configuration();
            config.configure("classpath:hibernate.cfg.xml");
 
            String path = URLDecoder.decode(this.getClass().getClassLoader().getResource("").getPath(), "utf-8");
            path += "resource/";
            File hbmDir = new File(path);
            if (hbmDir.exists())
            {
                List<File> fList = Arrays.asList(hbmDir.listFiles(filter(".hbm.xml")));
                if (null != fList && fList.size() > 0)
                {
                    for (File f : fList)
                    {
                        config.addResource("resource/" + f.getName());
                    }
                }
            }
            new SchemaUpdate(config).execute(truetrue);
        }
        catch (Exception e)
        {
            throwDatabaseException(e);
        }
        return true;
    }
}
三、BLL層
BLL層便是可以發布成Hessian服務的代碼,它也需要定義接口以及實現,下面是RoleBLL接口,獨自擁有的方法只有一個,就是列出指定角色下面的所有用戶。通用的方法以模版的方式定義在GenericBLL中。
/**
 * 邏輯層BLL接口-角色
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2013年7月30日下午6:19:38
 */
public interface RoleBLL extends GenericBLL<Role, Long>
{
    /**
     * 列出指定角色下面的所有用戶
     * 
     * @param role 角色名稱
     * @return 用戶列表 List<User>
     * @throws DatabaseException DAO層異常
     * @throws BusinessException 邏輯層異常
     */
    List<User> listUsers(String roleName) throws DatabaseException,
        BusinessException;     
}
 
/**
 * 各個BLL的通用模板
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2012年8月8日上午9:33:41
 * @param <T>
 * @param <ID>
 */
public interface GenericBLL<T, ID extends Serializable>
{
    List<T> findALL();
 
    T findById(ID id);
 
    T addT(T t);
 
    T editT(T t);
 
    boolean deleteT(T t);
 
    boolean deleteById(ID id);
}
 
RoleBLL接口的實現, GenericBLL接口的實現如下所示。 GenericBLLImpl幫助我們完成了七八成的功能,感謝 GenericBLLImpl。要注意的是,事務是在這一層統一配置的,在下面的代碼里給每一個業務邏輯都配置了事務,在類頭以Transactional標簽設置,只有當異常拋出時,回滾。同樣使用Spring標簽,將RolDAOImp 對象和 GenericBLLImpl對象注入進來。
/**
 * 邏輯BLL層-角色處理
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2013年7月30日下午6:36:10
 */
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
@Component("RoleBLLImpl")
public class RoleBLLImpl extends GenericBLLImpl<Role, Long> implements RoleBLL
{
    @Autowired
    @Qualifier("RoleDAOImpl")
    private RoleDAO          m_roleDAO;
     /**
     * 注入
     */
    @PostConstruct
    public void postConstruct()
    {
        super.setGenericDAO(m_roleDAO);
    }
     @Override
    public List<User> listRoleUsers(Role role) throws DatabaseException, BusinessException
    {
        List<User> users = m_roleDAO.findByRoleId(role.getId());
        return users;
    }
}
 
/**
 * 各個BLLImpl的通用模板
 * 
 * @author wanganqi
 * @version v1.0
 * @since 2012年8月8日下午2:20:29
 * @param <T>
 * @param <ID>
 */
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public abstract class GenericBLLImpl<T, ID extends Serializable> implements
    GenericBLL<T, ID>
{
    private GenericDAO<T, ID> genericDAO;
 
    public void setGenericDAO(GenericDAO<T, ID> genericDAO)
    {
        this.genericDAO = genericDAO;
    }
 
    public GenericBLLImpl(GenericDAO<T, ID> gd)
    {
        genericDAO = gd;
    }
 
    public GenericBLLImpl()
    {
    }
    public List<T> findALL()
    {
        List<T> retList = genericDAO.findAll();
        return retList;
    }
 
    public T findById(ID id)
    {
         T retT =  genericDAO .findById(id,  false );
        return retT;
    }
 
    public T addT(T t)
    {
        T rt = genericDAO.makePersistent(t);
        return rt;
    }
 
    public T saveOrUpdate(T entity)
    {
        T resultT = addT(entity);
        return resultT;
    }
 
    public T editT(T t)
    {
 
        return addT(t);
    }
 
    public boolean deleteT(T t)
    {
        if (null == t)
        {
            return true;
        }
        genericDAO.makeTransient(t);
        return true;
    }
 
    public boolean deleteById(ID id)
    {
        return deleteT(findById(id));
    }
}
 
四、發布成Hessian服務
 
由於使用了Spring框架,我們需要Spring相關的配置 。下面是基本的Spring配置,可以配置在Hessian服務層,也可以配置在DAL代碼層,由代碼維護Spring上下文。
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:aop="http://www.springframework.org/schema/aop"     xmlns:tx="http://www.springframework.org/schema/tx"     
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
                        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
                        http://www.springframework.org/schema/context  
                        http://www.springframework.org/schema/context/spring-context-3.2.xsd  
                        http://www.springframework.org/schema/tx  
                        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd  
                        http://cxf.apache.org/jaxws   
                        http://cxf.apache.org/schemas/jaxws.xsd">             
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
        p:configLocation="/WEB-INF/hibernate.cfg.xml">
    </bean>
 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />     
    <context:component-scan base-package="com.wang.anqi.model" />
    <context:component-scan base-package="com.wang.anqi.dao.impl" />       
    <!-- 發布bean -->       
    <bean id="roleBLLImpl" class="com.wang.anqi.dataBll.impl.RoleBLLImpl" />       
    <!-- 發布bean --> 
</beans>  
 
我們還需要hibernate.cfg.xml文件,這在“Model們”就已經說明了。如何發布成Hessian服務,發布Hessian服務需要注意些什么問題,如何調用Hessian服務,網上也有很多,我也會在以后的文章中總結出來,希望大家還能繼續關注。
上面的代碼雖然稍多了,有堆代碼的嫌疑,但畢竟這是必需的,要不然解釋不清。  






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM