Entity的继承以及泛型Dao(二)

一、继承与泛型都是因为懒

接着先前的例子,我们已经把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。

 

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top