首页 > 其他 > 详细

(14)SSH中BaseDao实现

时间:2016-07-29 19:12:14      阅读:219      评论:0      收藏:0      [点我收藏+]

在使用Hibernate的进行删除的时候,我们可以写这样的HQL语句:

对于Employee对象:

delete from Employee where empId=?

对于Department对象:

delete from Department where deptId=?

假如说要写一个BaseDao的删除方法时,就要进行一下抽象,得到下面的HQL语句

delete from 类名 where 主键属性=?


我们的代码应该能够让“类名”和“主键属性”能够“参数化”

1、“获取类名”问题的解决

IBaseDao.java

package com.rk.dao;

import java.io.Serializable;
import java.util.List;

public interface IBaseDao<T>
{
	T findById(Serializable id);
	void save(T instance);
	void update(T instance);
	void delete(Serializable id);
	List<T> findAll();
}

BaseDao.java

package com.rk.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.SessionFactory;

import com.rk.dao.IBaseDao;
import com.rk.utils.HibernateConfigurationUtils;

public abstract class BaseDao<T> implements IBaseDao<T>
{
	// 当前操作的实际的bean类型
	private Class<T> clazz;
	// 获取类名称
	private String className;
	
	// 反射泛型
	@SuppressWarnings("unchecked")
	public BaseDao()
	{
		Type type = this.getClass().getGenericSuperclass();
		// 转换为参数化类型
		ParameterizedType pt = (ParameterizedType) type;
		// 得到实际类型
		Type[] types = pt.getActualTypeArguments();
		// 获取实际类型
		clazz = (Class<T>) types[0];
		
		className = clazz.getSimpleName();
	}
	
	// 容器注入
	private SessionFactory sessionFactory;
	public void setSessionFactory(SessionFactory sessionFactory)
	{
		this.sessionFactory = sessionFactory;
	}
	public SessionFactory getSessionFactory()
	{
		return sessionFactory;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public T findById(Serializable id)
	{
		return (T) sessionFactory.getCurrentSession().get(clazz, id);
	}

	@Override
	public void save(T instance)
	{
		sessionFactory.getCurrentSession().save(instance);
	}

	@Override
	public void update(T instance)
	{
		sessionFactory.getCurrentSession().update(instance);
	}

	@Override
	public void delete(Serializable id)
	{
		String identifierPropertyName = HibernateConfigurationUtils.getIdentifierPropertyName(clazz);
		sessionFactory.getCurrentSession().createQuery("delete from "+className+" where "+ identifierPropertyName +"=?")
			.setParameter(0, id).executeUpdate();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findAll()
	{
		return sessionFactory.getCurrentSession().createQuery("from "+className).list();
	}

}


在上面的代码中,BaseDao()构造方法中,得到了泛型T的真实类型clazz,并将类的名字存储在className变量中,到这里就解决了“类名”参数化的问题。


2、“获取主键属性”问题解决

在上面代码的delete方法中,有如下代码:

String identifierPropertyName = HibernateConfigurationUtils.getIdentifierPropertyName(clazz);

identifierPropertyName就是主键属性,而HibernateConfigurationUtils是一个自定义的工具类。


HibernateConfigurationUtils.java

package com.rk.utils;

import java.util.Iterator;

import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;

/**
 * (Hibernate与Spring集成的情况下)
 * 根据实体类得到对应的表名、主键名、字段名和类的主键属性
 * 这里使用xml文件配置的映射,需要保证实体类名与对应映射文件名一致,即User.java与User.hbm.xml 
 *
 * 这里使用继承ApplicationContextAware的方式来获得ApplicationContext,
 * 因此需要在Spring配置文件中配置一下该类,才能自动注入ApplicationContext对象 
 */
public class HibernateConfigurationUtils implements ApplicationContextAware
{
	// 1、ApplicationContext对象
	private static ApplicationContext applicationContext;
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
	{
		HibernateConfigurationUtils.applicationContext = applicationContext;
	}
	
	// 2、Configuration对象
	private static Configuration configuration;
	public static Configuration getConfiguration()
	{
		if(configuration == null)
		{
			// 取sessionFactory的时候要加上&  
			LocalSessionFactoryBean factory = (LocalSessionFactoryBean) applicationContext.getBean("&sessionFactory");
			configuration = factory.getConfiguration();
		}
		return configuration;
	}
	public static void setConfiguration(Configuration configuration)
	{
		HibernateConfigurationUtils.configuration = configuration;
	}

	// 3、PersistentClass对象	
	private static <T> PersistentClass getPersistentClass(Class<T> clazz)
	{
		synchronized(HibernateConfigurationUtils.class)
		{
			PersistentClass pc = getConfiguration().getClassMapping(clazz.getSimpleName());
			if(pc == null)
			{
				configuration = configuration.addClass(clazz);
				pc = configuration.getClassMapping(clazz.getName());
			}
			return pc;
		}
	}
	
	// 4、获取映射信息
	// 4.1、得到类信息-类名
	public static <T> String getClassName(Class<T> clazz)
	{
		return getPersistentClass(clazz).getClassName();
	}
	// 4.2、得到类信息-主键属性
	public static <T> String getIdentifierPropertyName(Class<T> clazz)
	{
		return getPersistentClass(clazz).getIdentifierProperty().getName();
	}
	// 4.3、得到表信息-表名
	public static <T> String getTableName(Class<T> clazz)
	{
		return getPersistentClass(clazz).getTable().getName();
	}
	// 4.4、得到表信息-主键列
	public static <T> String getPKColumnName(Class<T> clazz)
	{
		return getPersistentClass(clazz).getTable().getPrimaryKey().getColumn(0).getName();
	}
	// 4.5、得到表信息-列名
	public static <T> String getColumnName(Class<T> clazz, String propertyName)
	{
		String columnName = "";
		PersistentClass persistentClass = getPersistentClass(clazz);
		Property property = persistentClass.getProperty(propertyName);
		Iterator<?> iterator = property.getColumnIterator();
		if(iterator.hasNext())
		{
			Column column = (Column) iterator.next();
			columnName += column.getName();
		}
		return columnName;
	}
}


上面代码主要分成4个部分:ApplicationContext-->Configuration-->PersistentClass-->获取映射信息。也就是说,由ApplicationContext对象得到Configuration对象,再由Configuration对象得到PersistentClass对象,最后由PersistentClass对象获取相关映射信息


其中,ApplicationContext对象由ApplicationContext接口通过DI的方式实现注入,因此需要将当前类加入到Spring的配置文件中,如下:

	<bean class="com.rk.utils.HibernateConfigurationUtils"></bean>


3、知识扩展

3.1、ApplicationContextAware

ApplicationContextAware是一个接口,位于org.springframework.context包中,只提供一个方法setApplicationContext(ApplicationContext applicationContext),以依赖注入(DI)的方式注入applicationContext对象。

package org.springframework.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;

/**
 * Interface to be implemented by any object that wishes to be notified
 * of the {ApplicationContext} that it runs in.
 *
 */
public interface ApplicationContextAware extends Aware {

	/**
	 * Set the ApplicationContext that this object runs in.
	 * Normally this call will be used to initialize the object.
	 */
	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

注意:实现ApplicationContextAware接口的类,要在Spring的配置文件中注册。


3.2、LocalSessionFactoryBean

LocalSessionFactoryBean位于org.springframework.orm.hibernate3包中。

继承关系:LocalSessionFactoryBean继承自AbstractSessionFactoryBean,而AbstractSessionFactoryBean又实现了FactoryBean<SessionFactory>接口。

public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware

public abstract class AbstractSessionFactoryBean extends HibernateExceptionTranslator
		implements FactoryBean<SessionFactory>, InitializingBean, DisposableBean 

public interface FactoryBean<T> 		


3.2.1、org.springframework.beans.factory.FactoryBean<T>


Interface to be implemented by objects used within a BeanFactory which are themselves factories. If a bean implements this interface, it is used as a factory for an object to expose, not directly as a bean instance that will be exposed itself. 


NB: A bean that implements this interface cannot be used as a normal bean. A FactoryBean is defined in a bean style, but the object exposed for bean references (getObject() is always the object that it creates. 

这里要做一下区分,有两种bean类型:一种是normal bean,另一种是factory bean。factory bean的特殊之处在于:它返回的对象并不是自身的实例,而是它创建的对象(也就是下面代码中通过T getObject()方法返回的对象)。

public interface FactoryBean<T> {

	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * As with a {@link BeanFactory}, this allows support for both the
	 * Singleton and Prototype design pattern.
	 */
	T getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates,
	 * or {@code null} if not known in advance.
	 */
	Class<?> getObjectType();

	/**
	 * Is the object managed by this factory a singleton? That is,
	 * will {@link #getObject()} always return the same object
	 * (a reference that can be cached)?
	 */
	boolean isSingleton();

}


3.2.2、AbstractSessionFactoryBean

全名:org.springframework.orm.hibernate3.AbstractSessionFactoryBean

Abstract org.springframework.beans.factory.FactoryBean that creates a Hibernate org.hibernate.SessionFactory within a Spring application context, providing general infrastructure not related to Hibernate‘s specific configuration API. 


This class mainly serves as common base class for LocalSessionFactoryBean.


在AbstractSessionFactoryBean类中,提供的T getObject()方法的实现。

public abstract class AbstractSessionFactoryBean extends HibernateExceptionTranslator
		implements FactoryBean<SessionFactory>, InitializingBean, DisposableBean {

	private DataSource dataSource;
	private SessionFactory sessionFactory;

	/**
	 * Set the DataSource to be used by the SessionFactory.
	 * If set, this will override corresponding settings in Hibernate properties.
	 *
	 * <p>If this is set, the Hibernate settings should not define
	 * a connection provider to avoid meaningless double configuration.
	 *
	 */
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	/**
	 * Return the DataSource to be used by the SessionFactory.
	 */
	public DataSource getDataSource() {
		return this.dataSource;
	}

	/**
	 * Return the singleton SessionFactory.
	 */
	public SessionFactory getObject() {		//注意:这个是FactoryBean<T>的方法
		return this.sessionFactory;
	}

	public Class<? extends SessionFactory> getObjectType() {
		return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class);
	}

	public boolean isSingleton() {
		return true;
	}

}


3.2.3、LocalSessionFactoryBean

全名:org.springframework.orm.hibernate3.LocalSessionFactoryBean

在Spring application context中创建一个共享的(shared) Hibernate SessionFactory,通常做法是:使用LocalSessionFactoryBean(实现了FactoryBean接口)来创建SessionFactory对象,接着将SessionFactory对象通过DI的方式注入到DAO层。

注意:这里是讲SpringHibernate整合,在Spring applicationContext中创建Hibernate SessionFactory。

org.springframework.beans.factory.FactoryBean that creates a Hibernate org.hibernate.SessionFactory. This is the usual way to set up a shared Hibernate SessionFactory in a Spring application context; the SessionFactory can then be passed to Hibernate-based DAOs via dependency injection.


上面这一段讲了:可以使用LocalSessionFactoryBean来创建SessionFactory对象。接着,需要对SessionFactory对象进行配置。对SessionFactory进行配置有两种方式:第1种是使用configLocation来引用Hibernate XML file(hibernate.cfg.xml),第2种是使用dataSource、hibernateProperties和mappingResources进行配置。

Configuration settings can either be read from a Hibernate XML file, specified as "configLocation", or completely via this class. A typical local configuration consists of one or more "mappingResources", various "hibernateProperties" (not strictly necessary), and a "dataSource" that the  SessionFactory should use. 


通过LocalSessionFactoryBean来创建SessionFactory的策略,适合于大部分的application。HibernateTransactionManager或者JtaTransactionManager可以用于transaction demarcation,其中JtaTransactionManager只有在multiple databases中是必要的。

This SessionFactory handling strategy is appropriate for most types of applications, from Hibernate-only single database apps to ones that need distributed transactions. Either HibernateTransactionManager or org.springframework.transaction.jta.JtaTransactionManager can be used for transaction demarcation, with the latter only necessary for transactions which span multiple databases.


This factory bean will by default expose a transaction-aware SessionFactory proxy, letting data access code work with the plain Hibernate SessionFactory and its getCurrentSession() method, while still being able to participate in current Spring-managed transactions: with any transaction management strategy, either local or JTA / EJB CMT, and any transaction synchronization mechanism, either Spring or JTA. Furthermore, getCurrentSession() will also seamlessly work with a request-scoped Session managed by org.springframework.orm.hibernate3.support.OpenSessionInViewFilter /  org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor.
public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware {


	private Class<? extends Configuration> configurationClass = Configuration.class;

	private Resource[] configLocations;

	private String[] mappingResources;

	private Resource[] mappingLocations;

	private Resource[] mappingDirectoryLocations;

	private Properties hibernateProperties;

	private Configuration configuration;

	/**
	 * Set the location of a single Hibernate XML config file, for example as
	 * classpath resource "classpath:hibernate.cfg.xml".
	 * <p>Note: Can be omitted when all necessary properties and mapping
	 * resources are specified locally via this bean.
	 * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
	 */
	public void setConfigLocation(Resource configLocation) {
		this.configLocations = new Resource[] {configLocation};
	}

	/**
	 * Set the locations of multiple Hibernate XML config files, for example as
	 * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
	 * <p>Note: Can be omitted when all necessary properties and mapping
	 * resources are specified locally via this bean.
	 * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
	 */
	public void setConfigLocations(Resource[] configLocations) {
		this.configLocations = configLocations;
	}

	/**
	 * Set Hibernate mapping resources to be found in the class path,
	 * like "example.hbm.xml" or "mypackage/example.hbm.xml".
	 * Analogous to mapping entries in a Hibernate XML config file.
	 * Alternative to the more generic setMappingLocations method.
	 * <p>Can be used to add to mappings from a Hibernate XML config file,
	 * or to specify all mappings locally.
	 * @see #setMappingLocations
	 * @see org.hibernate.cfg.Configuration#addResource
	 */
	public void setMappingResources(String[] mappingResources) {
		this.mappingResources = mappingResources;
	}

	/**
	 * Set locations of Hibernate mapping files, for example as classpath
	 * resource "classpath:example.hbm.xml". Supports any resource location
	 * via Spring‘s resource abstraction, for example relative paths like
	 * "WEB-INF/mappings/example.hbm.xml" when running in an application context.
	 * <p>Can be used to add to mappings from a Hibernate XML config file,
	 * or to specify all mappings locally.
	 * @see org.hibernate.cfg.Configuration#addInputStream
	 */
	public void setMappingLocations(Resource[] mappingLocations) {
		this.mappingLocations = mappingLocations;
	}

	/**
	 * Set locations of directories that contain Hibernate mapping resources,
	 * like "WEB-INF/mappings".
	 * <p>Can be used to add to mappings from a Hibernate XML config file,
	 * or to specify all mappings locally.
	 * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
	 */
	public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) {
		this.mappingDirectoryLocations = mappingDirectoryLocations;
	}

	/**
	 * Set Hibernate properties, such as "hibernate.dialect".
	 * <p>Can be used to override values in a Hibernate XML config file,
	 * or to specify all necessary properties locally.
	 * <p>Note: Do not specify a transaction provider here when using
	 * Spring-driven transactions. It is also advisable to omit connection
	 * provider settings and use a Spring-set DataSource instead.
	 * @see #setDataSource
	 */
	public void setHibernateProperties(Properties hibernateProperties) {
		this.hibernateProperties = hibernateProperties;
	}

	/**
	 * Return the Hibernate properties, if any. Mainly available for
	 * configuration through property paths that specify individual keys.
	 */
	public Properties getHibernateProperties() {
		if (this.hibernateProperties == null) {
			this.hibernateProperties = new Properties();
		}
		return this.hibernateProperties;
	}


	/**
	 * Return the Configuration object used to build the SessionFactory.
	 * Allows for access to configuration metadata stored there (rarely needed).
	 * @throws IllegalStateException if the Configuration object has not been initialized yet
	 */
	public final Configuration getConfiguration() {
		if (this.configuration == null) {
			throw new IllegalStateException("Configuration not initialized yet");
		}
		return this.configuration;
	}


	/**
	 * Allows for schema export on shutdown.
	 */
	@Override
	public void destroy() throws HibernateException {
		DataSource dataSource = getDataSource();
		if (dataSource != null) {
			// Make given DataSource available for potential SchemaExport,
			// which unfortunately reinstantiates a ConnectionProvider.
			configTimeDataSourceHolder.set(dataSource);
		}
		try {
			super.destroy();
		}
		finally {
			if (dataSource != null) {
				// Reset DataSource holder.
				configTimeDataSourceHolder.remove();
			}
		}
	}

}


其中有一点需要注意,在使用applicationContext获取LocalSessionFactoryBean时,需要添加“&”

// 取sessionFactory的时候要加上&  
LocalSessionFactoryBean factory = (LocalSessionFactoryBean) applicationContext.getBean("&sessionFactory");


我们看一下ApplicationContext的定义,ApplicationContext继承了ListableBeanFactory接口

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver

ListableBeanFactory接口继承了BeanFactory接口

public interface ListableBeanFactory extends BeanFactory

BeanFactory接口的定义,它有一个FACTORY_BEAN_PREFIX属性,通过它的注释,我们就知道了为什么要加“&”。

public interface BeanFactory {

	/**
	 * Used to dereference a {@link FactoryBean} instance and distinguish it from
	 * beans <i>created</i> by the FactoryBean. For example, if the bean named
	 * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
	 * will return the factory, not the instance returned by the factory.
	 */
	String FACTORY_BEAN_PREFIX = "&";

	/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * <p>This method allows a Spring BeanFactory to be used as a replacement for the
	 * Singleton or Prototype design pattern. Callers may retain references to
	 * returned objects in the case of Singleton beans.
	 * <p>Translates aliases back to the corresponding canonical bean name.
	 * Will ask the parent factory if the bean cannot be found in this factory instance.
	 * @param name the name of the bean to retrieve
	 * @return an instance of the bean
	 * @throws NoSuchBeanDefinitionException if there is no bean definition
	 * with the specified name
	 * @throws BeansException if the bean could not be obtained
	 */
	Object getBean(String name) throws BeansException;
}


3.3、Configuration

全名:org.hibernate.cfg.Configuration

当创建SessionFactory的时候,Configuration的实例用于指定属性(propeties)和映射文件(mapping documents)。通常情况下,一个application只对应一个Configuratioin,也只建立一个SessionFactory的实例,之后在线程(threads)当中创建Session。

An instance of Configuration allows the application to specify properties and mapping documents to be used when creating a SessionFactory. Usually an application will create a single Configuration, build a single instance of SessionFactory and then instantiate Sessions in threads servicing client requests. The Configuration is meant only as an initialization-time object. SessionFactorys are immutable and do not retain any association back to the Configuration.


A new Configuration will use the properties specified in hibernate.properties by default.

public class Configuration implements Serializable {

	protected Map<String, PersistentClass> classes;

	/**
	 * Iterate the entity mappings
	 *
	 * @return Iterator of the entity mappings currently contained in the configuration.
	 */
	public Iterator<PersistentClass> getClassMappings() {
		return classes.values().iterator();
	}

	/**
	 * Iterate the table mappings
	 *
	 * @return Iterator of the table mappings currently contained in the configuration.
	 */
	public Iterator<Table> getTableMappings() {
		return tables.values().iterator();
	}

	/**
	 * Get the mapping for a particular entity
	 *
	 * @param entityName An entity name.
	 * @return the entity mapping information
	 */
	public PersistentClass getClassMapping(String entityName) {
		return classes.get( entityName );
	}


}

其中,PersistentClass getClassMapping(String entityName) 方法用于得到PersistentClass。


3.4、PersistentClass

全名:org.hibernate.mapping.PersistentClass

/**
 * Mapping for an entity.
 *
 * @author Gavin King
 */
public abstract class PersistentClass implements Serializable, Filterable, MetaAttributable {

	private String entityName;
	private String className;

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className==null ? null : className.intern();
	}

	public abstract Property getIdentifierProperty();

	public abstract Table getTable();

}


参考:

 

Hibernate--根据实体类获得表名、主键名、字段名(与Spring集成)(一)

http://blog.csdn.net/zhangjk1993/article/details/40020813


 

Spring中的FactoryBean

http://blog.csdn.net/zhangjk1993/article/details/40017583



(14)SSH中BaseDao实现

原文:http://lsieun.blog.51cto.com/9210464/1831840

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!