使用泛型Entity在hibernate中的问题

在hibernate中,如果某个Entity的属性使用泛型,必须为其属性映射加上type属性。

 

public interface Entity<T> {
	T getId();
}

public class BaseEntity<T> implements Entity<T> {
	protected T id;
	
	public T getId() {
		return id;
	}
}

public class Forum extends BaseEntity<Long> {
// ...
}

在泛型DAO经常会看到这样的代码。

 

 Hibernate映射文件:

 

  <class name="marlon.myforum.domain.model.Forum" table="FORUM">
    <id name="id" column="ID" type="long">
      <generator class="native" />
    </id>
  </class>

 

如果不加上type="long",运行时就会错:

org.hibernate.MappingException: identifier mapping has wrong number of columns: Forum type: object
	at org.hibernate.mapping.RootClass.validate(RootClass.java:194)
	at org.hibernate.cfg.Configuration.validate(Configuration.java:1102)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1287)
 

从错误消息里可以看出hibernate认为id的type为"object",这是因为不指定type时,hibernate会自动根据属性的类型来决定,虽然在编译时id的类型为Long,但是由于泛型的Erase机制,编译之后类型信息就丢失了,通过反射得到id的类型为Object,这也就是为什么hibernate报错的原因。其实只要是通过反射来填充属性,都可能存在这种问题,比如在Web框架中,会将请求参数装载到一个bean中。假设如果id="abc",它仍然可以被填充到Forum的id属性中来,因为它的类型是一个Object,但是运行时通过forum.getId()时就会发生ClassCastException,这显然不是所期望的,这时id实际是一个String,现在需要换成Long。为了避免这种问题,对于上面的例子,可以将BaseEntity不声明为泛型,而直接容纳一个类型为Long的id,这样虽然没有将BaseEntity灵活,对ID其它类型的Entity需要直接实现Entity接口,但是却避免了使用泛型BaseEntity的缺点。

public class BaseEntity implements Entity<Long> {
	protected Long id;
	
	public Long getId() {
		return id;
	}
}
 

 

由此可见,用泛型也是需要慎重考虑的,虽然它能够带来编译时类型安全,但是不能忘记了编译后就是一个裸的Object(在没有指定限定符时),这在需要用反射中填充属性时尤其要注意,而现在大多数的Java框架又广泛的运用了此。

 

你可能感兴趣的:(DAO,框架,bean,Web,Hibernate)