package com.ycsoft.daos.core.impl; import static com.ycsoft.daos.helper.GenericsHelper.getGenericsClass; import static com.ycsoft.daos.helper.ListHelper.clearNullElement; import static com.ycsoft.daos.helper.ListHelper.getElementOfNotNull; import static org.apache.commons.lang.StringUtils.isNotBlank; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.PropertyUtils; import org.springframework.jdbc.core.JdbcTemplate; import com.ycsoft.commons.helper.StringHelper; import com.ycsoft.daos.config.Settings; import com.ycsoft.daos.config.Table; import com.ycsoft.daos.core.DataHandler; import com.ycsoft.daos.core.JDBCException; import com.ycsoft.daos.core.Pager; import com.ycsoft.daos.core.PartResultSetExtractor; import com.ycsoft.daos.core.Query; import com.ycsoft.daos.helper.BeanHelper; /** * 封装了对实体类常用的操作。使用该类需要指定实体类的类型, * 在实体类POJO中必须使用注释的方式配置实体类对应的表信息。 * * @see com.ycsoft.daos.config.POJO * * @author hh * @date Jan 13, 2010 7:22:51 PM */ @SuppressWarnings("unchecked") public class EntitySessionImpl<T> extends AbstractSessionImpl{ /** * */ private static final long serialVersionUID = 6993895118802243551L; //当前实体类的Class private Class entityClass ; public EntitySessionImpl(){ try { this.entityClass = getGenericsClass( getClass()); } catch(Exception e ) { try{ this.entityClass = getGenericsClass( getClass().getSuperclass()); }catch(Exception _e){ logger.error( getClass().getName()+"没有使用泛型指定实体类类型!!",_e); } } } /** * <p>获得下一个序列的值 , 并且提供了一个可格式化编号的空函数 * <code>formatSequence</code></p> * @return * @throws JDBCException */ public Serializable findSequence()throws JDBCException{ Table tb = Settings.getTable(getClass()); String sql = sqlGenerator.getSeqNextVal(tb.getSequenceName()); return formatSequence(getJdbcTemplate().queryForLong(sql)); } /** * <p>根据序列名称获取序列值 * <code>formatSequence</code></p> * @return * @throws JDBCException */ public Long findSequence(String seqName)throws JDBCException{ String sql = sqlGenerator.getSeqNextVal(seqName); return getJdbcTemplate().queryForLong(sql); } /** * <p> 将序列值转换成一定格式的主键值</p> * <p> 该函数是一个空函数,可以重写该函数 </p> * @param key 通过序列获得的主键值 * @return 格式化后的主键值 * @throws Exception */ protected Serializable formatSequence(Serializable key){ return key ; } /** * <p> 获得实体Bean对应表的所有记录 </p> */ public List<T> findAll() throws JDBCException { Table tb = Settings.getTable(getClass()); String sql = sqlGenerator.getEntityAll(tb.getTableName()); return createQuery(entityClass , sql).list(); } /** * <p>通过主键获得实体对象</p> * @param keyValue 主键字段值 */ public T findByKey(Serializable keyValue) throws JDBCException { Table tb = Settings.getTable(getClass()); if("".equals(tb.getTableName())){ throw new JDBCException("you need config pk for entity!"); } List<T> lst = createQuery(entityClass ,sqlGenerator.getEntityByKey(tb),keyValue).list(); if (lst.size() > 0)return lst.get(0); return null; } /** * 按给定的SQL命令及对应的参数。将结果集封装至集合列表中 * @throws JDBCException */ public List<T> findList(String sql, Object... params) throws JDBCException { List<T> lst = this.createQuery(entityClass, sql, params).list(); return lst; } /** * 按给定的SQL命令及对应的参数,查询一个实体对象, 如果结果集含有多个则只选择第一条记录。 * * @throws JDBCException */ public T findEntity(String sql , Object ...params) throws JDBCException{ List<T> lst = findList(sql, params); if(lst.size() > 0) return lst.get( 0 ); return null ; } /** * <p> 查询表中所有的数据并进行分页 </p> * <p> <code>page</code>需要设置的参数包括: * <ul><li> start : 开始位置 </li> * <li> limit : 每页显示的记录数 </li></ul></p> * @see com.ycsoft.daos.core.Pager * @param page 分页对象,查询的结果将设置到对象相应属性中 * @return 封装了总行数、结果集的page */ public Pager<T> findAll(Integer start,Integer limit) throws JDBCException { Table tb = Settings.getTable(getClass()); String sql = sqlGenerator.getEntityAll(tb.getTableName()); return createQuery( entityClass , sql) .setStart(start) .setLimit(limit) .page(); } /** * <p> 通过主键删除对应的记录,支持批量的主键删除 </p> * @param keys 主键值 * return 每行执行的结果集 */ public int[] remove(Serializable... keys) throws JDBCException { String sql = sqlGenerator.getDelete(Settings.getTable(getClass())); return executeBatch(sql, keys); } /** * <p>将指定的一个或多个实体对象持久化</p> * <p> 主键插入方式:自动查询当前实体类是否已经设置主键值, * 如果没有设置主键值,则通过给定的序列名获取虚列值,虚列值会调用<code>#formatSequence </code> * 函数格式化主键值,so 一般需要重写<code>#formatSequence </code>函数。 * </p> * @param entitys 需要保存的一个或多个实体对象 * @return 返回所执行的行数 查看JDBC 批量更新的返回值 */ public int[] save(T...entitys) throws JDBCException { if (entitys.length == 0) return null; Table table = Settings.getTable(getClass()); setColumns(table); List<String> lst = table.getColumns(); List<String> sqls = new ArrayList<String>(); try { for (T element : entitys) { Map<String , Object> maps = getElementOfNotNull(lst,element); if(isNotBlank(table.getPrimaryKey()) && !maps.containsKey(table.getPrimaryKey().toLowerCase()) && isNotBlank(table.getSequenceName())){ maps.put(table.getPrimaryKey(), findSequence()); } sqls.add(sqlGenerator.getSave( table.getTableName(), maps)); } } catch (Exception e) { throw new JDBCException("生成Save SQL语句出错!检查"+ table.getTableName() +"表结构column在JavaBean是否有对应的属性!" ,e); } String [] s = new String[sqls.size()]; sqls.toArray(s); return executeBatch(s); } /** * <p> 根据实体对应的表中的字段及属性值。如果属性值不能NULL,则将被更新。 * if need update all,you must see and use <tt>#update<tt> method</p> * @param entity 实体对象(支持多个) */ public int[] update(T ...entitys) throws JDBCException{ Table table = Settings.getTable(getClass()); if (StringHelper.isEmpty(table.getPrimaryKey())) throw new JDBCException("主键未设置,无法根据主键更新"); setColumns(table); List<String> lst = getColumnsNotContainsPK(table); List<String> sqls = new ArrayList<String>(); try { for (T element : entitys) { Map<String , Object> maps= getElementOfNotNull(lst,element); if (maps.size() == 0) continue; Object key = PropertyUtils.getProperty(element, table.getPrimaryKey().toLowerCase()); if (key ==null) throw new JDBCException("主键 "+table.getPrimaryKey()+" 值为空,无法根据主键更新"); sqls.add(sqlGenerator.getUpdate( table, maps , key )); } } catch (Exception e) { throw new JDBCException("生成Save SQL语句出错!检查"+ table.getTableName() +"表结构column在JavaBean是否有对应的属性!" ,e); } if (sqls.size()>0){ String [] s = new String[sqls.size()]; sqls.toArray(s); return executeBatch(s); } else { return null; } } /** * <p>查询记录,通过所传递的实体对象,根据实体对象中的参数值作为查询条件(NULL值除外), * 查询符合条件的记录</p> * @param entity 实体对象 * @return * @throws JDBCException */ public List<T> findByEntity(T entity) throws JDBCException { Map map; try { Table table = Settings.getTable(getClass()); setColumns(table); map = getElementOfNotNull(table.getColumns(),entity); clearNullElement(map); } catch (Exception e) { throw new JDBCException("将实体Bean转换到Map中出错!",e); } return findByMap(map); } /** * <p>查询记录,通过所传递的实体对象,根据实体对象中的参数值作为查询条件(NULL值除外), * 查询符合条件的记录,并进行分页</p> * @param entity 实体对象 * @return * @throws JDBCException */ public Pager<T> findByEntity(Integer start,Integer limit , T entity) throws JDBCException{ Map<String, Serializable> map; try { map = BeanHelper.describe(entity); clearNullElement(map); } catch (Exception e) { throw new JDBCException("将实体Bean转换到Map中出错!",e); } return findByMap(start,limit,map); } /** * <p> 通过map中的<tt>key-value</tt>作为查询条件,获取符合条件的记录 </p> * @param params 查询的条件 ,key:列名,value:值 * @return 查询的结果 * @throws JDBCException */ public List<T> findByMap(Map<String , Serializable> params) throws JDBCException { Table table = Settings.getTable(getClass()); String sql = sqlGenerator.getFindByMap(table.getTableName() , params); return createQuery( entityClass ,sql, params.values().toArray()).list(); } /** * <p> 通过map中的<tt>key-value</tt>作为查询条件,获取符合条件的记录,并进行分页 </p> * @param params 查询的条件 ,key:列名,value:值 * @return 查询的结果 * @throws JDBCException */ public Pager<T> findByMap(Integer start,Integer limit , Map<String , Serializable> params)throws JDBCException{ Table table = Settings.getTable(getClass()); String sql = sqlGenerator.getFindByMap(table.getTableName() , params); return createQuery( entityClass ,sql, params.values().toArray()) .setStart(start) .setLimit(limit) .page(); } /** * 实现父类的<code>createQuery</code>函数。 * 默认情况下,继承<code>EntitySessionImpl</code> * 应该使用该函数,而不需要在传入实体类的class * @throws Exception */ public Query<T> createQuery(String sql , Object... params) throws JDBCException { return createQuery( entityClass , sql, params ) ; } /** * 重载父类的createNameQuery,默认情况下,继承<code>EntitySessionImpl</code> * 应该使用该函数,而不需要在传入实体类的class */ protected Query<T> createNameQuery( String sql , Map<String , ? > params )throws JDBCException{ return createNameQuery(entityClass, sql, params); } /** * 使用默认的entityCls * @see #queryForResult(Class, DataHandler, int, String, Object...) */ public void queryForResult(DataHandler<T> dataHandler,int limit, String sql, Object...params) throws JDBCException { this.queryForResult(this.entityClass, dataHandler, limit, sql, params); } /** * 使用默认的entityCls * @see #queryForResult(Class, DataHandler, String, Object...) */ public void queryForResult(DataHandler<T> dataHandler, String sql, Object...params) throws JDBCException { this.queryForResult(this.entityClass, dataHandler, sql, params); } /** * <p> 获取对应表所有的字段名称并存储至table对象中 * 如果已经在此之前已经设置过,将不会在进行设置, * 该函数是在save、update时会被调用, * 需要根据表中的字段去获取实体类的值,而不是由实体类的属性映射成字段名 </p> */ private void setColumns(Table table)throws JDBCException{ if(null == table.getColumns()){ String sql = sqlGenerator.getTableColums(table.getTableName()); table.setColumns(findUniques(sql)); } } /** * <p>根据表返回表中除主键所有的列<p> * @param table * @return 返回表中除主键所有的列的List */ private List<String> getColumnsNotContainsPK(Table table){ List<String> lst = new ArrayList<String>(); lst.addAll(table.getColumns()); //因为table.columns中包含所有的列,更新不需要更新主键字段 lst.remove(table.getPrimaryKey().toLowerCase()); return lst; } public Class getEntityClass() { return entityClass; } }