一、继承与泛型都是因为懒
接着先前的例子,我们已经把Odds1x2与OddsHandicap这两个实体类的共同属性抽离出来放在他们的抽象父类Odds中。那么接下来要做的就是实现实体类相应的Dao类型。最简单的实现方法就是每个Entity对应一个Dao,每个Dao内都要实现对Entity的增删查改操作,但实际上大部分的增删查改操作都是类似的,只是处理不同的Entity而已。为了少写些重复代码,我们有完全正当的理由写一个泛型Dao来实现这些基本的增删查改操作。
仍然以我们的Odds为例子,假设我们现在只需要实现一个功能,就是查找某场比赛的某种赔率。
public interface OddsDao <T> { /** * 获取所有与某场赛事相关的有效赔率 * @param matchId * @return */ List<T> getAllValidByMatchId(long matchId); /** * 返回该泛型中用到的实际类型 * @return */ Class<T> getOddsType(); }
public abstract class OddsDaoHibernate<T> implements OddsDao<T> { @Autowired @Qualifier("betspiderSessionFactory") protected SessionFactory sessionFactory; private Class<T> oddsType; @SuppressWarnings("unchecked") public OddsDaoHibernate() { // 获得泛型的实际类型 this.oddsType = (Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; } @Override public Class<T> getOddsType() { return oddsType; } @SuppressWarnings("unchecked") @Override public List<T> getAllValidByMatchId(long matchId) { String hql = "from " + oddsType.getName() + " as odds" + " where odds.active = true and odds.matchId = :matchId"; Session session = sessionFactory.openSession(); List<T> oddsList = session.createQuery(hql).setLong("matchId", matchId).list(); session.close(); return oddsList; } }
那么,我们只需要使用OddsDaoHibernate<Odds1x2.class>,OddsDaoHibernate<OddsHandicap.class>,就可以对这两种OddsXXX对象进行增删查改操作了。
二、在Spring中注入泛型Dao
想要优雅的将OddsDaoHibernate<Odds1x2.class>,OddsDaoHibernate<OddsHandicap.class>注入到Spring容器中,我觉得最好的方式就是新建两个对应的类,直接继承OddsDaoHibernate泛型,并使用@Repository注解表示将这两个类型注入Spring bean容器。
@Repository public class Odds1x2DaoHibernate extends OddsDaoHibernate<Odds1x2> { }
@Repository public class OddsHandicapDaoHibernate extends OddsDaoHibernate<OddsHandicap> { }
但是实际使用这两个Dao进行Autowired依赖注入的时候,必须使用如下方式:
@Autowired @Qualifier("oddsHandicapDaoHibernate") // 使用Qualifier限定bean name private OddsDao<OddsHandicap> oddsDao; // 使用接口名来声明目标变量,而不能使用OddsHandicapDaoHibernate类名,因为实际的bean已经被Spring自动代理了 // 那个叫oddsHandicapDaoHibernate的Spring bean实际上是Proxy类型,该类型自动实现了OddsDao接口,并在内部包含了OddsDaoHibernate<OddsHandicap>对象
三、Service的泛型方法
上面这个例子由于需求比较简单,可以忽略Service层直接调用Dao。不过为了按三层架构的规范来,我们还是得考虑下Service层怎么写。我希望只写一个OddsService就能操作所有类型的Odds,这可以使用泛型方法实现。
public interface OddsService { /** * 获取与某场比赛相关的所有有效赔率 * @param oddsType 赔率类型 * @param matchId 比赛id * @return */ <T extends Odds> List<T> getAllValidByMatchId(Class<T> oddsType, long matchId); }
@Service public class OddsServiceImpl implements OddsService { @SuppressWarnings("rawtypes") private Map<Class, OddsDao> oddsDaoMap = new HashMap<Class, OddsDao>(); @SuppressWarnings("rawtypes") @Autowired private void setOddsDao(List<OddsDao> oddsDaos) { for (OddsDao oddsDao : oddsDaos) { oddsDaoMap.put(oddsDao.getOddsType(), oddsDao); } } @SuppressWarnings("unchecked") private <T extends Odds> OddsDao<T> getOddsDao(Class<T> oddsType) { return oddsDaoMap.get(oddsType); } @Override public <T extends Odds> List<T> getAllValidByMatchId(Class<T> oddsType, long matchId) { OddsDao<T> oddsDao = getOddsDao(oddsType); return oddsDao.getAllValidByMatchId(matchId); } }
setOddsDao使用@Autowired注解,那么在Spring的BeanFactory生成这个@Service bean的后,就会自动调用该方法来进行依赖注入,该函数的参数oddsDaos会被自动注入Spring容器中存在的所有类型为OddsDao的bean。然后我们在函数中将这些bean放在内部的Map成员中。getOddsDao泛型方法则从该Map中取出相应的OddsDao。