繁体   English   中英

Spring Data Repository StackOverflow

[英]Spring Data Repository StackOverflow

使用Spring Data Repositories时发现了一些奇怪的行为。

我写了这些类和接口:

@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();
    }
}

当我尝试测试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)

我看到save()方法存在一些问题。 delete()方法也会抛出stackoverflow。

我已经找到了解决方案。 当我更改将Repository接口扩展到(例如)JpaUserRepository的接口名称时,我的问题就消失了。

所以,问题是“发生了什么”。 为什么实现spring数据存储库的接口名称与模式Classname Repository匹配时抛出stackoverflow?

我的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>

好吧,问题不是因为Classname RepositoryImpl命名,而是因为RepositoryClassname Impl命名 你正在做的是告诉Spring你正在为RepositoryClassname接口上的某些方法提供自定义行为 也就是说,Spring正在为您实现JpaRepository中的所有标准CRUD方法。 您的实现允许您注入存储库bean,但它应该用作实现的一部分,而不仅仅用于委派 当你在Impl类中实现save()时, 你实际上为接口save()方法提供了一个自定义实现,所以,当你在“自定义”实现中调用接口save()时, 你正在强制一个无限循环最终得到stackoverflow异常。

如果您不打算提供自定义行为,请不要在Impl类中声明该方法。 另一方面,如果您不尝试为某些接口方法提供自定义行为,并且您只是尝试创建单独的存储库,请不要将其命名为RepositoryInterfaceName Impl。 如果您尝试修改实现,只需不要调用interface.save()方法。

PS:通常在持久层使用@Transactional是一种不好的做法,我认为在服务层使用@Transactional总是更好,因为它将处理你的业务逻辑。 查看此帖子了解更多信息。

暂无
暂无

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

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