简体   繁体   English

Spring Data Repository StackOverflow

[英]Spring Data Repository StackOverflow

Found some strange behavoir when working with Spring Data Repositories. 使用Spring Data Repositories时发现了一些奇怪的行为。

I wrote these classes and interfaces: 我写了这些类和接口:

@Transactional(readOnly = true)
public interface UserRepository extends Repository<User, Integer> {

    @Transactional
    @Modifying
    @Query("DELETE FROM User u WHERE u.id=:id")
    int delete(@Param("id") int id);

    @Transactional
    User save(User user);

    User findOne(Integer id);

    List<User> findAll();
}


public interface AbstractRepository<T> {

    T save(T user);

    // false if not found
    boolean delete(int id);

    // null if not found
    T get(int id);

    List<T> getAll();
    }



@Repository
public class UserRepositoryImpl implements AbstractRepository<User> {

    @Autowired
    private JpaUserRepository repository;

    @Override
    public User save(User user) {
        return repository.save(user);
    }

    @Override
    public boolean delete(int id) {
        return repository.delete(id) != 0;
    }

    @Override
    public User get(int id) {
        return repository.findOne(id);
    }

    @Override
    public List<User> getAll() {
        return repository.findAll();
    }
}

When I try to test UserRepositoryImpl, java.lang.StackOverflowError is thrown 当我尝试测试UserRepositoryImpl时,抛出java.lang.StackOverflowError

Caused by: java.lang.StackOverflowError
    at org.springframework.data.repository.util.ClassUtils.unwrapReflectionException(ClassUtils.java:166)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:505)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:478)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:115)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy55.save(Unknown Source)
    at ru.emitrohin.votingsystem.repository.UserRepositoryImpl.save(RestaurantRepositoryImpl.java:24)
    at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
    at  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

I see that there is some problem with save() method. 我看到save()方法存在一些问题。 Also stackoverflow is thrown on delete() method. delete()方法也会抛出stackoverflow。

I've already found the solution. 我已经找到了解决方案。 My problem disappears when I change name of interface that extends Repository interface to (for example) JpaUserRepository. 当我更改将Repository接口扩展到(例如)JpaUserRepository的接口名称时,我的问题就消失了。

So, the question is "What is going on". 所以,问题是“发生了什么”。 Why stackoverflow is thrown when interface name that implements spring data repository matches pattern Classname Repository? 为什么实现spring数据存储库的接口名称与模式Classname Repository匹配时抛出stackoverflow?

my pom.xml 我的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>xxx</groupId>
    <artifactId>xxx</artifactId>
    <packaging>war</packaging>

    <version>1.0-SNAPSHOT</version>

    <name>xxx</name>
    <url>xxx</url>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <tomcat.version>8.0.33</tomcat.version>
        <spring.version>4.3.4.RELEASE</spring.version>
        <spring-security.version>4.2.0.RELEASE</spring-security.version>
        <spring-data-jpa.version>1.10.4.RELEASE</spring-data-jpa.version>

        <!-- Logging -->
        <logback.version>1.1.7</logback.version>
        <slf4j.version>1.7.21</slf4j.version>

        <!--DB-->
        <postgresql.version>9.4.1211</postgresql.version>

        <!--Tests-->
        <junit.version>4.12</junit.version>

        <!-- Hibernate -->
        <hibernate.version>5.2.4.Final</hibernate.version>
        <hibernate-validator.version>5.3.2.Final</hibernate-validator.version>

        <!--Tools-->
        <ehcache.version>2.10.3</ehcache.version>

    </properties>

    <build>
        <finalName>xxx</finalName>
        <defaultGoal>package</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                </configuration>
            </plugin>

            <!--  http://stackoverflow.com/questions/4305935/is-it-possible-to-supply-tomcat6s-context-xml-file-via-the-maven-cargo-plugin#4417945 -->
            <plugin>
                <groupId>org.codehaus.cargo</groupId>
                <artifactId>cargo-maven2-plugin</artifactId>
                <version>1.5.0</version>
                <configuration>
                    <container>
                        <containerId>tomcat8x</containerId>
                        <systemProperties>
                            <file.encoding>UTF-8</file.encoding>
                            <spring.profiles.active>tomcat,datajpa</spring.profiles.active>
                        </systemProperties>
                        <dependencies>
                            <dependency>
                                <groupId>org.postgresql</groupId>
                                <artifactId>postgresql</artifactId>
                            </dependency>
                        </dependencies>
                    </container>
                    <configuration>
                        <configfiles>
                            <configfile>
                                <file>src/main/resources/tomcat/context.xml</file>
                                <todir>conf/Catalina/localhost/</todir>
                                <tofile>context.xml.default</tofile>
                            </configfile>
                        </configfiles>
                    </configuration>
                    <deployables>
                        <deployable>
                            <groupId>com.xxx</groupId>
                            <artifactId>xxx</artifactId>
                            <type>war</type>
                            <properties>
                                <context>${project.build.finalName}</context>
                            </properties>
                        </deployable>
                    </deployables>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>

        <!-- Logging with SLF4J & LogBack -->

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${spring-data-jpa.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- spring security-->

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <!--hibernate-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>${hibernate-validator.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>${ehcache.version}</version>
        </dependency>

        <!--Web-->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>${tomcat.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--Test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.2.21</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.8.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-hibernate5</artifactId>
            <version>2.8.4</version>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-library</artifactId>
            <version>1.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <profiles>
        <profile>
            <id>hsqldb</id>
            <dependencies>
                <dependency>
                    <groupId>org.hsqldb</groupId>
                    <artifactId>hsqldb</artifactId>
                    <version>2.3.4</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>${spring.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
</dependencyManagement>

Well, the problem comes not because of the Classname RepositoryImpl naming but because of the RepositoryClassname Impl naming . 好吧,问题不是因为Classname RepositoryImpl命名,而是因为RepositoryClassname Impl命名 What you are doing with this is telling Spring that you are provinding custom behaviour to some methods on the RepositoryClassname interface . 你正在做的是告诉Spring你正在为RepositoryClassname接口上的某些方法提供自定义行为 That said, Spring is implementing all the standard CRUD methods in JpaRepository for you. 也就是说,Spring正在为您实现JpaRepository中的所有标准CRUD方法。 Your implementation lets you inject the repository bean but it's meant to be used as part of the implementation and not just for delegating . 您的实现允许您注入存储库bean,但它应该用作实现的一部分,而不仅仅用于委派 When you implement save() in your Impl class, you are in fact giving a custom implementation for the interface save() method so, while you call the interface save() in your "custom" implementation, you are forcing an infinite loop that ends up with the stackoverflow exception. 当你在Impl类中实现save()时, 你实际上为接口save()方法提供了一个自定义实现,所以,当你在“自定义”实现中调用接口save()时, 你正在强制一个无限循环最终得到stackoverflow异常。

If you are not going to give a custom behaviour just don't declare the method in your Impl class. 如果您不打算提供自定义行为,请不要在Impl类中声明该方法。 On the other hand, if you are not trying to give custom behaviour to some interface methods, and you were just trying to create a separate repository, don't name it RepositoryInterfaceName Impl. 另一方面,如果您不尝试为某些接口方法提供自定义行为,并且您只是尝试创建单独的存储库,请不要将其命名为RepositoryInterfaceName Impl。 If you were trying to modify the implementation, just don't call the interface.save() method. 如果您尝试修改实现,只需不要调用interface.save()方法。

PS: usually it's a bad practice to use @Transactional at persistence layer, I think it's always better to use @Transactional on the service layer, as it's going to handle your business logic. PS:通常在持久层使用@Transactional是一种不好的做法,我认为在服务层使用@Transactional总是更好,因为它将处理你的业务逻辑。 Check this post for more information. 查看此帖子了解更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM