通过接口的惰性代理(具有最终方法的实体)
Lazy proxies through interfaces (entities with final methods)
我在我的项目中有一个要求,即我的所有方法都应该是抽象的或最终的(请不要争论这个要求——我知道它很愚蠢,只是假设它在那里)。
这是 Hibernate 映射实体的问题,因为 Hibernate 需要在 运行 时间内创建代理,以便能够在延迟加载时初始化关系。无法覆盖 setter 方法会导致根本无法加载这些方法(确实执行了查询,但从未填充对象)。
If the final class does implement a proper interface, you could alternatively tell Hibernate to use the interface instead when generating the proxies. See Example 4.4, “Proxying an interface in hbm.xml” and Example 4.5, “Proxying an interface in annotations”.
示例:
@Entity @Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
所以理论上可以只告诉 hibernate 实现一个接口而不是扩展原来的 class。
我试过这个解决方案,但我的问题出在关系本身。这是一个过度简化的例子:
@Entity
@Proxy(proxyClass = ICat.class)
@Table(name = "cat")
public class Cat implements ICat {
@Id
private Long catId;
@OneToMany(mappedBy = "cat", fetch = FetchType.LAZY)
private List<Kitten> kittens;
...
}
@Entity
@Proxy(proxyClass = IKitten.class)
@Table(name="kitten")
public class Cat implements IKitten {
@Id
private Long kittenId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="catId")
private Cat cat;
...
}
现在,如果我尝试获取一个 Cat
对象,我会得到一个 ClassCastException
,因为它试图将一个 IKitten
集合转换为一个 Kitten
集合。这让我认为我应该使用接口而不是实现来声明关系——这也会产生编译时错误,因为我的接口从未被声明为实体,但实现是(这在文档的示例中有明确说明)。
我该如何解决这个问题?
我之前也有同样的需求,通过声明final all classes绕过它。我的休眠结构没有代理,因此我不知道这是否能解决问题,但这是要求。如果您使用 mockito,则会出现一个附带问题。记住这一点。
顺便说一下,您的代码中显示的第二个 class 有错字,应命名为 Kitten
您可以在 one-to-many
和 many-to-one
关联中使用该接口,但您需要在 targetEntity
属性中提供实际的 Class。关系应该是这样的:
@Entity
@Proxy(proxyClass = ICat.class)
@Table(name = "cat")
public class Cat implements ICat {
@Id
private Long catId;
@OneToMany(mappedBy = "cat", fetch = FetchType.LAZY, targetEntity=Cat.class)
private List<IKitten> kittens;
...
}
@Entity
@Proxy(proxyClass = IKitten.class)
@Table(name="kitten")
public class Cat implements IKitten {
@Id
private Long kittenId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="catId", targetEntity=Cat.class)
private ICat cat;
...
}
我在我的项目中有一个要求,即我的所有方法都应该是抽象的或最终的(请不要争论这个要求——我知道它很愚蠢,只是假设它在那里)。
这是 Hibernate 映射实体的问题,因为 Hibernate 需要在 运行 时间内创建代理,以便能够在延迟加载时初始化关系。无法覆盖 setter 方法会导致根本无法加载这些方法(确实执行了查询,但从未填充对象)。
If the final class does implement a proper interface, you could alternatively tell Hibernate to use the interface instead when generating the proxies. See Example 4.4, “Proxying an interface in hbm.xml” and Example 4.5, “Proxying an interface in annotations”.
示例:
@Entity @Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
所以理论上可以只告诉 hibernate 实现一个接口而不是扩展原来的 class。
我试过这个解决方案,但我的问题出在关系本身。这是一个过度简化的例子:
@Entity
@Proxy(proxyClass = ICat.class)
@Table(name = "cat")
public class Cat implements ICat {
@Id
private Long catId;
@OneToMany(mappedBy = "cat", fetch = FetchType.LAZY)
private List<Kitten> kittens;
...
}
@Entity
@Proxy(proxyClass = IKitten.class)
@Table(name="kitten")
public class Cat implements IKitten {
@Id
private Long kittenId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="catId")
private Cat cat;
...
}
现在,如果我尝试获取一个 Cat
对象,我会得到一个 ClassCastException
,因为它试图将一个 IKitten
集合转换为一个 Kitten
集合。这让我认为我应该使用接口而不是实现来声明关系——这也会产生编译时错误,因为我的接口从未被声明为实体,但实现是(这在文档的示例中有明确说明)。
我该如何解决这个问题?
我之前也有同样的需求,通过声明final all classes绕过它。我的休眠结构没有代理,因此我不知道这是否能解决问题,但这是要求。如果您使用 mockito,则会出现一个附带问题。记住这一点。
顺便说一下,您的代码中显示的第二个 class 有错字,应命名为 Kitten
您可以在 one-to-many
和 many-to-one
关联中使用该接口,但您需要在 targetEntity
属性中提供实际的 Class。关系应该是这样的:
@Entity
@Proxy(proxyClass = ICat.class)
@Table(name = "cat")
public class Cat implements ICat {
@Id
private Long catId;
@OneToMany(mappedBy = "cat", fetch = FetchType.LAZY, targetEntity=Cat.class)
private List<IKitten> kittens;
...
}
@Entity
@Proxy(proxyClass = IKitten.class)
@Table(name="kitten")
public class Cat implements IKitten {
@Id
private Long kittenId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="catId", targetEntity=Cat.class)
private ICat cat;
...
}