DAO开发模式介绍
学术部 张亚涛
2008-8-28
一 .有关DAO模式的介绍
业务对象只应该关注业务逻辑,不应该关心数据存取的细节。数据访问对象必须实现特定的持久化策略(如,基于JDBC或Hibernate的持久化逻辑), 这样就抽出来了DAO层,作为数据源层,而之上的Domain Model层与之通讯而已,如果将那些实现了数据访问操作的所有细节都放入高层Domain model(领域模型)的话,系统的结构一定层次上来说就变得有些混乱。低级别的数据访问逻辑与高级别的业务逻辑分离,用一个DAO接口隐藏持久化操作的 细节,这样使用的最终目的就是让业务对象无需知道底层的持久化技术知识,这是标准 j2ee 设计模式之一。一个典型的的DAO组成:DAO工厂类,DAO接口,实现DAO接口的具体类(每个 DAO 实例负责一个主要域对象或实体),VO(Value Object)。如果一个DAO 工厂只为一个数据库的实现(现在只考虑这种情况)而创建很多的DAO的时候,实现该策略时,我们考虑采用工厂方法设计模 式.
二.设计DAO要注意的问题
在采用这种工厂方法设计模式来实现时我们其实要注意很多问题,哪个对象负责开始事务,哪个负责事务结束?DAO 是否要负责事务的开始和结束? 应用程序是否需要通过多少个DAO访问数据?事务涉及一个DAO还是多个DAO?一个DAO是否调用另一个DAO的方法?了解上述问题的答案将有助于我们 选择最适合的 DAO 的事务界定策略。在 DAO 中有两种主要的界定事务的策略。一种方式是让 DAO 负责界定事务,另一种将事务界定交给调用这个 DAO 方法的对象处理。如果选择了前一种方式,那么就将事务代码嵌入到 DAO 中。如果选择后一种方式,那么事务界定代码就是在 DAO 类外面,在这里我将用<<Hibernate项目开发宝典>>中留言版的小例子来理解后一种工作方式是如何工作的,以及如何自己 实现一个类似Spring的IOC轻量级容器中Bean工厂的功能(当然是没有使用Spring应用程序框架的情况下,对于这个简单的例子来说更有助于我 们理解Spring的DI模式)。这个小实例所要实现的业务功能包括创建用户,用户登录,发表文章,浏览文章,修改文章和删除文章,所以有两个对应的实体 对象User,Message。本文不涉及到业务逻辑,以及显示层部分。
三.DAO的实现
DAO 模式对开发J2EE应用的人员来说都应该很熟悉的,但是模式的实现各不相同,在这里我将按下面的思路来实现:
1.系统中的所有数据库访问都通过 DAO 进行以实现封装。
2.每个 DAO 实例负责一个主要域对象或实体。
3.DAO 负责域对象的创建、读取(按主键)、更新和删除(CRUD)。
4.DAO 可允许基于除主键之外的标准进行查询,返回值通常是DAO 负责的域对象集合。
5.像上面说的,DAO 不负责处理事务、会话或连接,而把这交给一个工具类,这样做是为了实现灵活性。
(一)泛型 DAO 接口
泛型 DAO 的基础是其 CRUD 操作。下面的接口定义泛型 DAO 的方法:
提供数据库操作接口给业务层使用
清单1
public interface IMessageDAO
{
//对应留言信息Message这个实体对象的操作
public void saveMessage( Message message );
public void updateMessage( Message message );
public List getMessages( );
public void deleteMessage( String id, String userId );
public Message getMessage( String id );
}
清单2
public interface IUserDAO
{
public void saveUser( User user );
public User getUser( String username );
public User getUserById( String id );
}
(二)泛型DAO的实现
第一个泛型 DAO 的实现
DAO 的实现类,封装数据库逻辑,按<<Hibernate项目开发宝典>>书上所说的将那些持久化操作封装到一个DAO基础类,也相 当于是一个工具类,通过继承这个基础类,DAO的实现类可以在很大程度上简化持久化操作的步骤,减少代码的重复量。这个基础类命名为 HibernateDAO,具体的方法实现如清单2
清单 3.
/**
* 使用Hibernate实现DAO的基础类
* 包括了持久化操作的一些基础方法
*/
public class HibernateDAO
{
/**
* 保存对象信息到数据库
* @param obj 需要进行持久化操作的对象
*/
public void saveObject(Object obj)
{
HibernateUtil.getCurrentSession().save(obj);
}
/**
* 更新持久化对象
* @param obj 需要更新的对象
*/
public void updateObject(Object obj)
{
HibernateUtil.getCurrentSession().update(obj);
}
/**
* 使用HQL语句进行查询
* @param hsql 查询语句
* @return 符合条件的对象集合
*/
public List getObjects(String hsql)
{
List result = HibernateUtil.getCurrentSession().createQuery(hsql).list();
return result;
}
/**
* 使用HQL语句进行对象的查询
* @param hsql 查询语句
* @return 符合条件的对象
*/
public Object getObject(String hsql)
{
Object result = HibernateUtil.getCurrentSession().createQuery(hsql).uniqueResult();
return result;
}
/**
* 根据ID值得到持久化的对象
* @param cls 对象的类型
* @param id ID值
* @return 指定ID的对象
*/
public Object getObject(Class cls, String id)
{
Object result = HibernateUtil.getCurrentSession().get(cls, id);
return result;
}
/**
* 删除对象信息
* @param obj 被删除的对象
*/
public void deleteObject(Object obj)
{
HibernateUtil.getCurrentSession().delete(obj);
}
}
清单 4. IMessageDAO接口的实现类
/**
* IMessageDAO接口的Hibernate实现
*/
public class MessageDAO extends HibernateDAO implements IMessageDAO
{
/**
* 保存留言信息
*
* @param message
* 被保存的留言对象
*/
public void saveMessage(Message message)
{
super.saveObject(message);
}
/**
* 得到所有的留言信息
*
* @return 返回所有的留言信息
*/
public List getMessages()
{
String hsql = "from Message";
return super.getObjects(hsql);
}
/**
* 删除留言信息
*
* @param id
* 要删除的留言信息的ID值
* @param userId
* 执行删除操作的用户ID
*/
public void deleteMessage(String id, String userId)
{
Message msg = getMessage(id);
if (msg == null)
{
throw new MessageDAOException("找不到你所要删除的留言!");
}
if (!msg.getUser().getId().equals(userId))
{
throw new MessageDAOException("你不能删除别人的留言!");
}
deleteObject(msg);
}
/**
* 得到留言信息
*
* @param id
* 留言的ID值
* @return 指定ID值得留言对象
*/
public Message getMessage(String id)
{
return (Message) getObject(Message.class, id);
}
/**
* 更新留言信息
*
* @param message
* 欲更新的留言对象
*/
public void updateMessage(Message message)
{
updateObject(message);
}
}
清单 5. IUserDAO接口的实现类
/**
* IUserDAO接口的Hibernate实现
*/
public class UserDAO extends HibernateDAO implements IUserDAO
{
/**
* 保存用户信息到数据库
* @param user 被保存的用户对象
*/
public void saveUser(User user)
{
if (user == null)
return;
User u = getUser(user.getName());
if (u != null)
throw new MessageDAOException("用户名已经存在,请使用其它用户名!");
saveObject(user);
}
/**
* 得到用户对象
* @param username 用户的登录名
* @return 指定登录名的用户对象
*/
public User getUser(String username)
{
User u = (User) getObject("from User u where u.name = '" + username
+ "'");
return u;
}
/**
* 得到用户对象的信息
* @param id 用户的ID值
* @return 指定的用户信息
*/
public User getUserById(String id)
{
return (User) getObject(User.class, id);
}
}
四.事务界定
前面说过, DAO 不负责处理事务、会话或连接,而把这交给一个工具类,封装所有关于数据库的操作。把Session的获取,语句的关闭等放在这个类更好。通常的设计把数据 库的代码放到DAO的实现类中,这样如果某个DAO实现类设计不良,要改动就必须牵涉到很多地方,不利于维护。在这里的工具类代码如清单6。
清单 6.
public class HibernateUtil
{
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final String INTERCEPTOR_CLASS = "hibernate.util.interceptor_class";
private static Configuration configuration;
private static SessionFactory sessionFactory;
private static ThreadLocal threadSession = new ThreadLocal();
private static ThreadLocal threadTransaction = new ThreadLocal();
private static boolean useThreadLocal = true;
static {
// Create the initial SessionFactory from the default configuration files
try {
// Replace with Configuration() if you don't use annotations or JDK 5.0
//configuration = new AnnotationConfiguration();
configuration = new Configuration();
// Read not only hibernate.properties, but also hibernate.cfg.xml
configuration.configure();
// Assign a global, user-defined interceptor with no-arg constructor
String interceptorName = configuration.getProperty(INTERCEPTOR_CLASS);
if (interceptorName != null) {
Class interceptorClass =
HibernateUtil.class.getClassLoader().loadClass(interceptorName);
Interceptor interceptor = (Interceptor)interceptorClass.newInstance();
configuration.setInterceptor(interceptor);
}
// Disable ThreadLocal Session/Transaction handling if CMT is used
if (org.hibernate.transaction.CMTTransactionFactory.class.getName()
.equals( configuration.getProperty(Environment.TRANSACTION_STRATEGY) ) )
useThreadLocal = false;
if (configuration.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
// Let Hibernate bind it to JNDI
configuration.buildSessionFactory();
} else {
// or use static variable handling
sessionFactory = configuration.buildSessionFactory();
}
} catch (Throwable ex) {
// We have to catch Throwable, otherwise we will miss
// NoClassDefFoundError and other subclasses of Error
log.error("Building SessionFactory failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
/**
* Returns the original Hibernate configuration.
*
* @return Configuration
*/
public static Configuration getConfiguration() {
return configuration;
}
/**
* Returns the global SessionFactory.
*
* @return SessionFactory
*/
public static SessionFactory getSessionFactory() {
SessionFactory sf = null;
String sfName = configuration.getProperty(Environment.SESSION_FACTORY_NAME);
if ( sfName != null) {
log.debug("Looking up SessionFactory in JNDI.");
try {
sf = (SessionFactory) new InitialContext().lookup(sfName);
} catch (NamingException ex) {
throw new RuntimeException(ex);
}
} else {
sf = sessionFactory;
}
if (sf == null)
throw new IllegalStateException("SessionFactory not available.");
return sf;
}
/**
* Closes the current SessionFactory and releases all resources.
* <p>
* The only other method that can be called on HibernateUtil
* after this one is rebuildSessionFactory(Configuration).
*/
public static void shutdown() {
log.debug("Shutting down Hibernate.");
// Close caches and connection pools
getSessionFactory().close();
// Clear static variables
configuration = null;
sessionFactory = null;
// Clear ThreadLocal variables
threadSession.set(null);
threadTransaction.set(null);
}
/**
* Rebuild the SessionFactory with the static Configuration.
* <p>
* This method also closes the old SessionFactory before, if still open.
* Note that this method should only be used with static SessionFactory
* management, not with JNDI or any other external registry.
*/
public static void rebuildSessionFactory() {
log.debug("Using current Configuration for rebuild.");
rebuildSessionFactory(configuration);
}
/**
* Rebuild the SessionFactory with the given Hibernate Configuration.
* <p>
* HibernateUtil does not configure() the given Configuration object,
* it directly calls buildSessionFactory(). This method also closes
* the old SessionFactory before, if still open.
*
* @param cfg
*/
public static void rebuildSessionFactory(Configuration cfg) {
log.debug("Rebuilding the SessionFactory from given Configuration.");
synchronized(sessionFactory) {
if (sessionFactory != null && !sessionFactory.isClosed())
sessionFactory.close();
if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null)
cfg.buildSessionFactory();
else
sessionFactory = cfg.buildSessionFactory();
configuration = cfg;
}
}
/**
* Retrieves the current Session local to the thread.
* <p/>
* If no Session is open, opens a new Session for the running thread.
* If CMT is used, returns the Session bound to the current JTA
* container transaction. Most other operations on this class will
* then be no-ops or not supported, the container handles Session
* and Transaction boundaries, ThreadLocals are not used.
*
* @return Session
*/
public static Session getCurrentSession() {
if (useThreadLocal) {
Session s = (Session) threadSession.get();
if (s == null) {
log.debug("Opening new Session for this thread.");
s = getSessionFactory().openSession();
threadSession.set(s);
}
return s;
} else {
return getSessionFactory().getCurrentSession();
}
}
/**
* Closes the Session local to the thread.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions.
*/
public static void closeSession() {
if (useThreadLocal) {
Session s = (Session) threadSession.get();
threadSession.set(null);
Transaction tx = (Transaction) threadTransaction.get();
if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack()) )
throw new IllegalStateException("Closing Session but Transaction still open!");
if (s != null && s.isOpen()) {
log.debug("Closing Session of this thread.");
s.close();
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous close call.");
}
}
/**
* Start a new database transaction.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions. In both cases, it will either
* start a new transaction or join the existing ThreadLocal or JTA
* transaction.
*/
public static void beginTransaction() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
if (tx == null) {
log.debug("Starting new database transaction in this thread.");
tx = getCurrentSession().beginTransaction();
threadTransaction.set(tx);
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous tx begin call.");
}
}
/**
* Commit the database transaction.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions. It will commit the
* ThreadLocal or BMT/JTA transaction.
*/
public static void commitTransaction() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
try {
if ( tx != null && !tx.wasCommitted()
&& !tx.wasRolledBack() ) {
log.debug("Committing database transaction of this thread.");
tx.commit();
}
threadTransaction.set(null);
} catch (RuntimeException ex) {
log.error(ex);
rollbackTransaction();
throw ex;
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous tx commit call.");
}
}
/**
* Rollback the database transaction.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions. It will rollback the
* resource local or BMT/JTA transaction.
*/
public static void rollbackTransaction() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
try {
threadTransaction.set(null);
if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) {
log.debug("Tyring to rollback database transaction of this thread.");
tx.rollback();
log.debug("Database transaction rolled back.");
}
} catch (RuntimeException ex) {
throw new RuntimeException("Might swallow original cause, check ERROR log!", ex);
} finally {
closeSession();
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous tx rollback call.");
}
}
/**
* Reconnects a Hibernate Session to the current Thread.
* <p>
* Unsupported in a CMT environment.
*
* @param session The Hibernate Session to be reconnected.
*/
public static void reconnect(Session session) {
if (useThreadLocal) {
log.debug("Reconnecting Session to this thread.");
session.reconnect();
threadSession.set(session);
} else {
log.error("Using CMT/JTA, intercepted not supported reconnect call.");
}
}
/**
* Disconnect and return Session from current Thread.
*
* @return Session the disconnected Session
*/
public static Session disconnectSession() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack()) )
throw new IllegalStateException("Disconnecting Session but Transaction still open!");
Session session = getCurrentSession();
threadSession.set(null);
if (session.isConnected() && session.isOpen()) {
log.debug("Disconnecting Session from this thread.");
session.disconnect();
}
return session;
} else {
log.error("Using CMT/JTA, intercepted not supported disconnect call.");
return null;
}
}
/**
* Register a Hibernate interceptor with the current SessionFactory.
* <p>
* Every Session opened is opened with this interceptor after
* registration. Has no effect if the current Session of the
* thread is already open, effective on next close()/getCurrentSession().
* <p>
* Attention: This method effectively restarts Hibernate. If you
* need an interceptor active on static startup of HibernateUtil, set
* the <tt>hibernateutil.interceptor</tt> system property to its
* fully qualified class name.
*/
public static void registerInterceptorAndRebuild(Interceptor interceptor) {
log.debug("Setting new global Hibernate interceptor and restarting.");
configuration.setInterceptor(interceptor);
rebuildSessionFactory();
}
public static Interceptor getInterceptor() {
return configuration.getInterceptor();
}
}
上 面的代码中,如果是使用Hibernate3.1以上版本对Session的管理进行了优化,提供了内建的Session管理方式,所以上面也可以不用 ThreadLocal类型的实例对象来保存。书中提到一点,现在绝大多数的应用都是基于Web来实现的,这里通过Web所提供的Filter机制实现持 久化操作的进一步的封装,将一个用户请求中所做的所有持久化操作看成一个事务,当然,如果某个业务确实需要将这个请求分解成多个事务,那么也可以在业务实 现的方法中自行地进行事务的提交或者回流操作,完成的Hibernate如清单7
清单7
public class HibernateFilter implements Filter
{
private static Log log = LogFactory.getLog(HibernateFilter.class);
public void init(FilterConfig filterConfig) throws ServletException {
log.info("Servlet filter init, now opening/closing a Session for each request.");
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
// There is actually no explicit "opening" of a Session, the
// first call to HibernateUtil.beginTransaction() in control
// logic (e.g. use case controller/event handler, or even a
// DAO factory) will get a fresh Session.
try
{
HibernateUtil.beginTransaction();
request.setCharacterEncoding( "gb2312" );
chain.doFilter(request, response);
// Commit any pending database transaction.
HibernateUtil.commitTransaction();
}
catch (ServletException ex)
{
log.debug("Rolling back the database transaction.");
HibernateUtil.rollbackTransaction(); // Also closes the Session
// Just rollback and let others handle the exception, e.g. for display
throw ex;
}
catch (IOException ex)
{
log.debug("Rolling back the database transaction.");
HibernateUtil.rollbackTransaction(); // Also closes the Session
// Just rollback and let others handle the exception, e.g. for display
throw ex;
}
finally
{
// No matter what happens, close the Session.
HibernateUtil.closeSession();
}
}
public void destroy() {}
}
经过学习上面的数据库操作封装,事务界定,在对于如何在数据访问对象上应用DAO模式以及有了更深入的理解了~
学术部 张亚涛
2008-8-28
一 .有关DAO模式的介绍
业务对象只应该关注业务逻辑,不应该关心数据存取的细节。数据访问对象必须实现特定的持久化策略(如,基于JDBC或Hibernate的持久化逻辑), 这样就抽出来了DAO层,作为数据源层,而之上的Domain Model层与之通讯而已,如果将那些实现了数据访问操作的所有细节都放入高层Domain model(领域模型)的话,系统的结构一定层次上来说就变得有些混乱。低级别的数据访问逻辑与高级别的业务逻辑分离,用一个DAO接口隐藏持久化操作的 细节,这样使用的最终目的就是让业务对象无需知道底层的持久化技术知识,这是标准 j2ee 设计模式之一。一个典型的的DAO组成:DAO工厂类,DAO接口,实现DAO接口的具体类(每个 DAO 实例负责一个主要域对象或实体),VO(Value Object)。如果一个DAO 工厂只为一个数据库的实现(现在只考虑这种情况)而创建很多的DAO的时候,实现该策略时,我们考虑采用工厂方法设计模 式.
二.设计DAO要注意的问题
在采用这种工厂方法设计模式来实现时我们其实要注意很多问题,哪个对象负责开始事务,哪个负责事务结束?DAO 是否要负责事务的开始和结束? 应用程序是否需要通过多少个DAO访问数据?事务涉及一个DAO还是多个DAO?一个DAO是否调用另一个DAO的方法?了解上述问题的答案将有助于我们 选择最适合的 DAO 的事务界定策略。在 DAO 中有两种主要的界定事务的策略。一种方式是让 DAO 负责界定事务,另一种将事务界定交给调用这个 DAO 方法的对象处理。如果选择了前一种方式,那么就将事务代码嵌入到 DAO 中。如果选择后一种方式,那么事务界定代码就是在 DAO 类外面,在这里我将用<<Hibernate项目开发宝典>>中留言版的小例子来理解后一种工作方式是如何工作的,以及如何自己 实现一个类似Spring的IOC轻量级容器中Bean工厂的功能(当然是没有使用Spring应用程序框架的情况下,对于这个简单的例子来说更有助于我 们理解Spring的DI模式)。这个小实例所要实现的业务功能包括创建用户,用户登录,发表文章,浏览文章,修改文章和删除文章,所以有两个对应的实体 对象User,Message。本文不涉及到业务逻辑,以及显示层部分。
三.DAO的实现
DAO 模式对开发J2EE应用的人员来说都应该很熟悉的,但是模式的实现各不相同,在这里我将按下面的思路来实现:
1.系统中的所有数据库访问都通过 DAO 进行以实现封装。
2.每个 DAO 实例负责一个主要域对象或实体。
3.DAO 负责域对象的创建、读取(按主键)、更新和删除(CRUD)。
4.DAO 可允许基于除主键之外的标准进行查询,返回值通常是DAO 负责的域对象集合。
5.像上面说的,DAO 不负责处理事务、会话或连接,而把这交给一个工具类,这样做是为了实现灵活性。
(一)泛型 DAO 接口
泛型 DAO 的基础是其 CRUD 操作。下面的接口定义泛型 DAO 的方法:
提供数据库操作接口给业务层使用
清单1
public interface IMessageDAO
{
//对应留言信息Message这个实体对象的操作
public void saveMessage( Message message );
public void updateMessage( Message message );
public List getMessages( );
public void deleteMessage( String id, String userId );
public Message getMessage( String id );
}
清单2
public interface IUserDAO
{
public void saveUser( User user );
public User getUser( String username );
public User getUserById( String id );
}
(二)泛型DAO的实现
第一个泛型 DAO 的实现
DAO 的实现类,封装数据库逻辑,按<<Hibernate项目开发宝典>>书上所说的将那些持久化操作封装到一个DAO基础类,也相 当于是一个工具类,通过继承这个基础类,DAO的实现类可以在很大程度上简化持久化操作的步骤,减少代码的重复量。这个基础类命名为 HibernateDAO,具体的方法实现如清单2
清单 3.
/**
* 使用Hibernate实现DAO的基础类
* 包括了持久化操作的一些基础方法
*/
public class HibernateDAO
{
/**
* 保存对象信息到数据库
* @param obj 需要进行持久化操作的对象
*/
public void saveObject(Object obj)
{
HibernateUtil.getCurrentSession().save(obj);
}
/**
* 更新持久化对象
* @param obj 需要更新的对象
*/
public void updateObject(Object obj)
{
HibernateUtil.getCurrentSession().update(obj);
}
/**
* 使用HQL语句进行查询
* @param hsql 查询语句
* @return 符合条件的对象集合
*/
public List getObjects(String hsql)
{
List result = HibernateUtil.getCurrentSession().createQuery(hsql).list();
return result;
}
/**
* 使用HQL语句进行对象的查询
* @param hsql 查询语句
* @return 符合条件的对象
*/
public Object getObject(String hsql)
{
Object result = HibernateUtil.getCurrentSession().createQuery(hsql).uniqueResult();
return result;
}
/**
* 根据ID值得到持久化的对象
* @param cls 对象的类型
* @param id ID值
* @return 指定ID的对象
*/
public Object getObject(Class cls, String id)
{
Object result = HibernateUtil.getCurrentSession().get(cls, id);
return result;
}
/**
* 删除对象信息
* @param obj 被删除的对象
*/
public void deleteObject(Object obj)
{
HibernateUtil.getCurrentSession().delete(obj);
}
}
清单 4. IMessageDAO接口的实现类
/**
* IMessageDAO接口的Hibernate实现
*/
public class MessageDAO extends HibernateDAO implements IMessageDAO
{
/**
* 保存留言信息
*
* @param message
* 被保存的留言对象
*/
public void saveMessage(Message message)
{
super.saveObject(message);
}
/**
* 得到所有的留言信息
*
* @return 返回所有的留言信息
*/
public List getMessages()
{
String hsql = "from Message";
return super.getObjects(hsql);
}
/**
* 删除留言信息
*
* @param id
* 要删除的留言信息的ID值
* @param userId
* 执行删除操作的用户ID
*/
public void deleteMessage(String id, String userId)
{
Message msg = getMessage(id);
if (msg == null)
{
throw new MessageDAOException("找不到你所要删除的留言!");
}
if (!msg.getUser().getId().equals(userId))
{
throw new MessageDAOException("你不能删除别人的留言!");
}
deleteObject(msg);
}
/**
* 得到留言信息
*
* @param id
* 留言的ID值
* @return 指定ID值得留言对象
*/
public Message getMessage(String id)
{
return (Message) getObject(Message.class, id);
}
/**
* 更新留言信息
*
* @param message
* 欲更新的留言对象
*/
public void updateMessage(Message message)
{
updateObject(message);
}
}
清单 5. IUserDAO接口的实现类
/**
* IUserDAO接口的Hibernate实现
*/
public class UserDAO extends HibernateDAO implements IUserDAO
{
/**
* 保存用户信息到数据库
* @param user 被保存的用户对象
*/
public void saveUser(User user)
{
if (user == null)
return;
User u = getUser(user.getName());
if (u != null)
throw new MessageDAOException("用户名已经存在,请使用其它用户名!");
saveObject(user);
}
/**
* 得到用户对象
* @param username 用户的登录名
* @return 指定登录名的用户对象
*/
public User getUser(String username)
{
User u = (User) getObject("from User u where u.name = '" + username
+ "'");
return u;
}
/**
* 得到用户对象的信息
* @param id 用户的ID值
* @return 指定的用户信息
*/
public User getUserById(String id)
{
return (User) getObject(User.class, id);
}
}
四.事务界定
前面说过, DAO 不负责处理事务、会话或连接,而把这交给一个工具类,封装所有关于数据库的操作。把Session的获取,语句的关闭等放在这个类更好。通常的设计把数据 库的代码放到DAO的实现类中,这样如果某个DAO实现类设计不良,要改动就必须牵涉到很多地方,不利于维护。在这里的工具类代码如清单6。
清单 6.
public class HibernateUtil
{
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final String INTERCEPTOR_CLASS = "hibernate.util.interceptor_class";
private static Configuration configuration;
private static SessionFactory sessionFactory;
private static ThreadLocal threadSession = new ThreadLocal();
private static ThreadLocal threadTransaction = new ThreadLocal();
private static boolean useThreadLocal = true;
static {
// Create the initial SessionFactory from the default configuration files
try {
// Replace with Configuration() if you don't use annotations or JDK 5.0
//configuration = new AnnotationConfiguration();
configuration = new Configuration();
// Read not only hibernate.properties, but also hibernate.cfg.xml
configuration.configure();
// Assign a global, user-defined interceptor with no-arg constructor
String interceptorName = configuration.getProperty(INTERCEPTOR_CLASS);
if (interceptorName != null) {
Class interceptorClass =
HibernateUtil.class.getClassLoader().loadClass(interceptorName);
Interceptor interceptor = (Interceptor)interceptorClass.newInstance();
configuration.setInterceptor(interceptor);
}
// Disable ThreadLocal Session/Transaction handling if CMT is used
if (org.hibernate.transaction.CMTTransactionFactory.class.getName()
.equals( configuration.getProperty(Environment.TRANSACTION_STRATEGY) ) )
useThreadLocal = false;
if (configuration.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
// Let Hibernate bind it to JNDI
configuration.buildSessionFactory();
} else {
// or use static variable handling
sessionFactory = configuration.buildSessionFactory();
}
} catch (Throwable ex) {
// We have to catch Throwable, otherwise we will miss
// NoClassDefFoundError and other subclasses of Error
log.error("Building SessionFactory failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
/**
* Returns the original Hibernate configuration.
*
* @return Configuration
*/
public static Configuration getConfiguration() {
return configuration;
}
/**
* Returns the global SessionFactory.
*
* @return SessionFactory
*/
public static SessionFactory getSessionFactory() {
SessionFactory sf = null;
String sfName = configuration.getProperty(Environment.SESSION_FACTORY_NAME);
if ( sfName != null) {
log.debug("Looking up SessionFactory in JNDI.");
try {
sf = (SessionFactory) new InitialContext().lookup(sfName);
} catch (NamingException ex) {
throw new RuntimeException(ex);
}
} else {
sf = sessionFactory;
}
if (sf == null)
throw new IllegalStateException("SessionFactory not available.");
return sf;
}
/**
* Closes the current SessionFactory and releases all resources.
* <p>
* The only other method that can be called on HibernateUtil
* after this one is rebuildSessionFactory(Configuration).
*/
public static void shutdown() {
log.debug("Shutting down Hibernate.");
// Close caches and connection pools
getSessionFactory().close();
// Clear static variables
configuration = null;
sessionFactory = null;
// Clear ThreadLocal variables
threadSession.set(null);
threadTransaction.set(null);
}
/**
* Rebuild the SessionFactory with the static Configuration.
* <p>
* This method also closes the old SessionFactory before, if still open.
* Note that this method should only be used with static SessionFactory
* management, not with JNDI or any other external registry.
*/
public static void rebuildSessionFactory() {
log.debug("Using current Configuration for rebuild.");
rebuildSessionFactory(configuration);
}
/**
* Rebuild the SessionFactory with the given Hibernate Configuration.
* <p>
* HibernateUtil does not configure() the given Configuration object,
* it directly calls buildSessionFactory(). This method also closes
* the old SessionFactory before, if still open.
*
* @param cfg
*/
public static void rebuildSessionFactory(Configuration cfg) {
log.debug("Rebuilding the SessionFactory from given Configuration.");
synchronized(sessionFactory) {
if (sessionFactory != null && !sessionFactory.isClosed())
sessionFactory.close();
if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null)
cfg.buildSessionFactory();
else
sessionFactory = cfg.buildSessionFactory();
configuration = cfg;
}
}
/**
* Retrieves the current Session local to the thread.
* <p/>
* If no Session is open, opens a new Session for the running thread.
* If CMT is used, returns the Session bound to the current JTA
* container transaction. Most other operations on this class will
* then be no-ops or not supported, the container handles Session
* and Transaction boundaries, ThreadLocals are not used.
*
* @return Session
*/
public static Session getCurrentSession() {
if (useThreadLocal) {
Session s = (Session) threadSession.get();
if (s == null) {
log.debug("Opening new Session for this thread.");
s = getSessionFactory().openSession();
threadSession.set(s);
}
return s;
} else {
return getSessionFactory().getCurrentSession();
}
}
/**
* Closes the Session local to the thread.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions.
*/
public static void closeSession() {
if (useThreadLocal) {
Session s = (Session) threadSession.get();
threadSession.set(null);
Transaction tx = (Transaction) threadTransaction.get();
if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack()) )
throw new IllegalStateException("Closing Session but Transaction still open!");
if (s != null && s.isOpen()) {
log.debug("Closing Session of this thread.");
s.close();
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous close call.");
}
}
/**
* Start a new database transaction.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions. In both cases, it will either
* start a new transaction or join the existing ThreadLocal or JTA
* transaction.
*/
public static void beginTransaction() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
if (tx == null) {
log.debug("Starting new database transaction in this thread.");
tx = getCurrentSession().beginTransaction();
threadTransaction.set(tx);
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous tx begin call.");
}
}
/**
* Commit the database transaction.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions. It will commit the
* ThreadLocal or BMT/JTA transaction.
*/
public static void commitTransaction() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
try {
if ( tx != null && !tx.wasCommitted()
&& !tx.wasRolledBack() ) {
log.debug("Committing database transaction of this thread.");
tx.commit();
}
threadTransaction.set(null);
} catch (RuntimeException ex) {
log.error(ex);
rollbackTransaction();
throw ex;
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous tx commit call.");
}
}
/**
* Rollback the database transaction.
* <p>
* Is a no-op (with warning) if called in a CMT environment. Should be
* used in non-managed environments with resource local transactions, or
* with EJBs and bean-managed transactions. It will rollback the
* resource local or BMT/JTA transaction.
*/
public static void rollbackTransaction() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
try {
threadTransaction.set(null);
if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) {
log.debug("Tyring to rollback database transaction of this thread.");
tx.rollback();
log.debug("Database transaction rolled back.");
}
} catch (RuntimeException ex) {
throw new RuntimeException("Might swallow original cause, check ERROR log!", ex);
} finally {
closeSession();
}
} else {
log.warn("Using CMT/JTA, intercepted superfluous tx rollback call.");
}
}
/**
* Reconnects a Hibernate Session to the current Thread.
* <p>
* Unsupported in a CMT environment.
*
* @param session The Hibernate Session to be reconnected.
*/
public static void reconnect(Session session) {
if (useThreadLocal) {
log.debug("Reconnecting Session to this thread.");
session.reconnect();
threadSession.set(session);
} else {
log.error("Using CMT/JTA, intercepted not supported reconnect call.");
}
}
/**
* Disconnect and return Session from current Thread.
*
* @return Session the disconnected Session
*/
public static Session disconnectSession() {
if (useThreadLocal) {
Transaction tx = (Transaction) threadTransaction.get();
if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack()) )
throw new IllegalStateException("Disconnecting Session but Transaction still open!");
Session session = getCurrentSession();
threadSession.set(null);
if (session.isConnected() && session.isOpen()) {
log.debug("Disconnecting Session from this thread.");
session.disconnect();
}
return session;
} else {
log.error("Using CMT/JTA, intercepted not supported disconnect call.");
return null;
}
}
/**
* Register a Hibernate interceptor with the current SessionFactory.
* <p>
* Every Session opened is opened with this interceptor after
* registration. Has no effect if the current Session of the
* thread is already open, effective on next close()/getCurrentSession().
* <p>
* Attention: This method effectively restarts Hibernate. If you
* need an interceptor active on static startup of HibernateUtil, set
* the <tt>hibernateutil.interceptor</tt> system property to its
* fully qualified class name.
*/
public static void registerInterceptorAndRebuild(Interceptor interceptor) {
log.debug("Setting new global Hibernate interceptor and restarting.");
configuration.setInterceptor(interceptor);
rebuildSessionFactory();
}
public static Interceptor getInterceptor() {
return configuration.getInterceptor();
}
}
上 面的代码中,如果是使用Hibernate3.1以上版本对Session的管理进行了优化,提供了内建的Session管理方式,所以上面也可以不用 ThreadLocal类型的实例对象来保存。书中提到一点,现在绝大多数的应用都是基于Web来实现的,这里通过Web所提供的Filter机制实现持 久化操作的进一步的封装,将一个用户请求中所做的所有持久化操作看成一个事务,当然,如果某个业务确实需要将这个请求分解成多个事务,那么也可以在业务实 现的方法中自行地进行事务的提交或者回流操作,完成的Hibernate如清单7
清单7
public class HibernateFilter implements Filter
{
private static Log log = LogFactory.getLog(HibernateFilter.class);
public void init(FilterConfig filterConfig) throws ServletException {
log.info("Servlet filter init, now opening/closing a Session for each request.");
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
// There is actually no explicit "opening" of a Session, the
// first call to HibernateUtil.beginTransaction() in control
// logic (e.g. use case controller/event handler, or even a
// DAO factory) will get a fresh Session.
try
{
HibernateUtil.beginTransaction();
request.setCharacterEncoding( "gb2312" );
chain.doFilter(request, response);
// Commit any pending database transaction.
HibernateUtil.commitTransaction();
}
catch (ServletException ex)
{
log.debug("Rolling back the database transaction.");
HibernateUtil.rollbackTransaction(); // Also closes the Session
// Just rollback and let others handle the exception, e.g. for display
throw ex;
}
catch (IOException ex)
{
log.debug("Rolling back the database transaction.");
HibernateUtil.rollbackTransaction(); // Also closes the Session
// Just rollback and let others handle the exception, e.g. for display
throw ex;
}
finally
{
// No matter what happens, close the Session.
HibernateUtil.closeSession();
}
}
public void destroy() {}
}
经过学习上面的数据库操作封装,事务界定,在对于如何在数据访问对象上应用DAO模式以及有了更深入的理解了~
相关推荐
2. **Dao开发模式**: 在Mybatis中,Dao层通常由接口和XML配置文件(或注解)组成。接口定义了业务方法,而XML配置文件则映射SQL语句。当调用接口方法时,Mybatis会根据配置动态生成实现类,执行对应的SQL。 3. **...
DAO(Data Access Object)开发模式是一种常见的软件设计模式,它主要用于隔离业务逻辑层与数据访问层,使得业务对象可以专注于自身的业务逻辑,而不需关心数据的存储和访问细节。DAO模式在Java EE应用中尤其常见,...
dao模式的实现 及数据库介绍
在本项目中,我们采用的是基于JSP+Servlet+JavaBean+DAO的经典Web开发模式,用于构建一个学生体质管理系统。这种模式是Java Web开发中常用的一种架构,它将业务逻辑、数据访问和用户界面进行了清晰的分离,提高了...
DAO(Data Access Object)设计模式是软件开发中一种常见的用于处理数据访问的模式,它将业务逻辑与数据访问逻辑分离,使得代码结构更加清晰,更易于维护和测试。在Java开发中,DAO模式广泛应用于数据库操作,为应用...
【标题】"jsp+dao+servlet实现新闻发布系统增删改"是基于Web开发的一个经典案例,主要用于学习和实践Java Web开发技术。这个系统的核心在于利用Java Servlet处理HTTP请求,JSP进行视图展示,以及DAO(Data Access ...
DAO(Data Access Object)设计模式是Java Web开发中常用的一种设计模式,它的主要目的是将业务逻辑与数据访问操作分离,提高代码的可复用性和可维护性。在本精讲中,我们将深入探讨DAO设计模式的原理、实现方式以及...
下面,我们将深入探讨DAO设计模式的核心概念、实现方式以及它在实际开发中的应用。 DAO设计模式的基本思想是为数据库操作创建一个独立的接口,这个接口称为DAO接口。通过这个接口,业务层可以调用各种数据访问方法...
在Java Web开发中,MVC(Model-View-Controller)三层架构和DAO(Data Access Object)工厂模式是两种常见的设计模式,它们对于构建高效、可维护的系统至关重要。本项目结合这两种模式,旨在提高代码组织的清晰度和...
#### 二、代理DAO开发模式 ##### 2.1 Mapper接口 在MyBatis中,Mapper接口充当着DAO的角色。当使用代理开发模式时,我们只需要定义一个接口,而不需要具体实现该接口,MyBatis会自动生成一个实现类,这个实现类...
在Web开发中,DAO(Data Access Object)设计模式是一种常见的用于分离业务逻辑层与数据访问层的架构模式。DAO的主要目的是为了提供一个统一的接口,使得业务逻辑组件可以与数据库交互而无需直接操作具体的数据库API...
DAO设计模式是软件开发中处理数据库操作的一种常见手段,它有助于保持代码结构清晰,降低维护成本。通过学习本教程,你应该能理解和掌握DAO模式的基本概念,以及如何在实际项目中应用和扩展它。
**JSP留言管理系统Ⅵ基于MVC+DAO设计模式详解** 在Web开发中,MVC(Model-View-Controller)设计模式和DAO(Data Access ...无论是对于初学者还是经验丰富的开发者,这样的项目都是理解和实践Web开发模式的良好案例。
数据库:SQLite 代码:JSP 目的要求 (1)掌握Servlet使用技术; (2)掌握开发平台的分层技术; (3)掌握数据访问的封装技术。 ...(1)建立项目,创建...(1)Servlet+Service+DAO开发模式的理解; (2)DAO的封装。
总结来说,DAO设计模式在Java开发中扮演着重要的角色,尤其是在处理数据持久化问题时,它提供了一种灵活且可扩展的方式来管理数据访问,降低了系统的复杂性,提高了代码的可维护性。通过将数据访问逻辑封装在DAO中,...
- **封装数据访问**:DAO模式将对数据库或其它数据源的访问操作进行封装,使开发人员能够以统一的方式访问数据库。 - **降低耦合度**:通过DAO模式,业务逻辑层不再直接依赖于特定的数据源实现细节,提高了系统的...
在本主题中,我们将深入探讨Java DAO开发的核心概念、设计模式以及实现方法。 1. **DAO设计模式** DAO设计模式是一种软件设计模式,它提供了一个接口,用于访问和操作持久化存储中的对象,如数据库记录。通过使用...
DAO模式在实际开发中广泛应用,尤其是在Java和.NET等面向对象编程语言中。 ### DAO设计模式的核心概念 1. **数据访问对象(DAO)**:DAO是接口或类,它定义了对数据源进行操作的一系列方法。这些方法通常包括增、...
在Web开发中,DAO(Data Access Object)模式是一种常见的设计模式,用于封装对数据库的操作,将业务逻辑与数据访问层分离,提高代码的可维护性和可测试性。本教程将详细介绍如何使用JSP结合DAO模式来构建一个具备...