Spring将CGLIB代理对象传递给@Repository层和Hibernate

时间:2014-10-12 10:25:34

标签: spring spring-aop cglib

这是一个概念性问题,即

我正在使用Spring Application Context来实例化和管理我的对象。我有一个由以下层组成的应用程序。

模型(DAO),存储库,服务

现在在Repository和Service层中,所有Classes都实现了相应的接口,我可以通过调用相应接口的方法来调用这些层的方法,例如:

@Autowired
public IUserRepository iUserRepository;
iUserRepository.doSomething();

在我的模型(DAO)层中,我有使用

注释的类
@Repository
@Entity
@Table(name = "USERS")
public class Users {
...
}

我面临的问题是流动。在我的服务层中,当我尝试从模型(DAO)层实例化一个对象,即:

Users userTest =  (Users)context.getBean("users");

我得到“用户”类型的对象,但这不是“用户”对象,而是通过基于CGLIB的代理实例化的代理对象。 该代理对象具有其他属性。请参阅调试中的附图。 enter image description here

当我尝试将其进一步传递到我的存储库层时,我得到下面列出的错误

如果我使用运算符“new”实例化同一个类的对象,那么一切都运行良好,即我能够将该对象保存在数据库中。

user = new Users();

iUserRepository.createUser(user);

直到现在我已经知道我不在Spring中使用“new”运算符,即Spring Application context(container)shell管理所有对象但是如何处理这种情况。

我的DAO Users对象确实有一个默认构造函数但没有实现任何接口,因此没有使用JdkDynamicAopProxy,而是创建了基于CGLIB的代理。但是该代理对象以后不被接受为我的存储库的User对象,即我得到“org.hibernate.MappingException:Unknown entity:”错误消息。

问题是什么是“正确的”构造/星座,即可以使用新的运算符,还是我犯了一些我还没有意识到的典型错误。

2014-10-12 11:13:53.707 ERROR 8388 --- [           main] o.s.R.account.UserRepositoryImpl         : Exception

org.hibernate.MappingException: Unknown entity: org.syncServer.Model.acount.Users$$EnhancerBySpringCGLIB$$54139ff4
    at org.hibernate.internal.SessionFactoryImpl.getEntityPersister(SessionFactoryImpl.java:1095)
    at org.hibernate.internal.SessionImpl.getEntityPersister(SessionImpl.java:1439)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:116)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:209)
    at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:194)
    at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
    at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:711)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:703)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:698)
    at org.syncServer.Repository.account.UserRepositoryImpl.create(UserRepositoryImpl.java:186)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy76.create(Unknown Source)
    at org.syncServer.Service.account.UserServiceImpl.create(UserServiceImpl.java:266)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy81.create(Unknown Source)
    at org.syncServer.core.Tests.createUser(Tests.java:44)
    at org.syncServer.core.Application.main(Application.java:95)

2 个答案:

答案 0 :(得分:2)

好像你正试图通过mixin直接使用spring-data Repositories创建一个Active Record

虽然很多人都不接受,但恕我直言这种方法可以通过组合更好地工作,即在entites中注入存储库,而不是通过cglib代理扩展它们。

例如:

public interface ActiveRecord {
    void save();
    User load(Long id);
    void delete();
}

interface UserRepository extends JpaRepository<User, Long> {

}

@Configurable
class User implements ActiveRecord {
    @Id
    private Long id;
    private String name;
    @Transient @Autowired 
    private UserRepository repository;

    @Override
    public void save() {
        this.repository.save(this);

    }
    @Override
    public User load(Long id) {
        return repository.findOne(id);

    }
    @Override
    public void delete() {
        this.repository.delete(this);
    }
}

请注意,您需要使用AspectJ进行编织,因为实体通常由ORM或应用程序代码创建,而不是由Spring容器创建。

User user = new User();
user.setName("test");
user.save();

答案 1 :(得分:0)

我遇到了类似的问题。这是因为默认情况下,Hibernate使用entity.getClass().getName()来解析实体名称,如的最后一行所示。  下面来自类CoordinatingEntityNameResolver

的反编译代码
 public String resolveEntityName(Object entity) {
        String entityName = this.interceptor.getEntityName(entity);
        if (entityName != null) {
            return entityName;
        } else {
            Iterator var3 = this.sessionFactory.getMetamodel().getEntityNameResolvers().iterator();

            while(var3.hasNext()) {
                EntityNameResolver resolver = (EntityNameResolver)var3.next();
                entityName = resolver.resolveEntityName(entity);
                if (entityName != null) {
                    break;
                }
            }

            return entityName != null ? entityName : entity.getClass().getName();
        }
    }

在第一行中,您可以看到它首先尝试使用hibernate's interceptor来解析名称。

您可以自定义拦截器并覆盖实体名称解析逻辑,如下所示

public class EntityProxySupportHibernateInterceptor extends EmptyInterceptor {
    @Override
    public String getEntityName(Object object) {
        return AopUtils.getTargetClass(object).getName();
    }
}

在我的情况下,我使用Spring的AopUitls.getTargetClass(),因为我是使用Spring的aop代理按实体包装的。如果您使用其他类型的代理(例如原始的cglib),则可以适当地更改实现。

然后将其注册到application.properties

spring.jpa.properties.hibernate.ejb.interceptor=com.your.package.EntityProxySupportHibernateInterceptor