web-dev-qa-db-fra.com

Comment puis-je exécuter une procédure stockée avec JPA et Spring Data?

J'essaie d'appeler le Terminal_GetTicket procédure stockée dans ma base de données mais continue d'obtenir l'exception suivante:

PropertyReferenceException: No property getTicket found for type TicketInfo

J'ai validé ma configuration de manière croisée avec une entité de test très simple et tout semble bien fonctionner, mais pour le cas réel, quelque chose ne va pas.

Voici mon entité de domaine (TicketInfo):

@Entity
@NamedStoredProcedureQuery(name = "TicketInfo.getTicket", procedureName = "Terminal_GetTicket", resultClasses = TicketInfo.class, parameters = { 
    @StoredProcedureParameter(mode = ParameterMode.IN, name = "sys_id_game", type = Integer.class)})
public class TicketInfo {

    @Id @GeneratedValue
    private Long id;
    private String idTicket;
    private Integer externalTicketCode;
    private Short sequenseAlert;
    private Integer dlTimeStamp;

Toutes les variables d'instance ont leurs getters et setters correctement définis et la procédure stockée a un total de 5 paramètres de sortie correspondant aux attributs de TicketInfo.

De plus, voici mon interface de dépôt:

public interface TicketInfoRepository extends CrudRepository<TicketInfo, Long> {
    @Transactional(timeout = 5)
    @Procedure
    TicketInfo getTicket(Integer sys_id_game);
}

En outre, voici mon context.xml fichier (pour Spring):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:repository="http://www.springframework.org/schema/data/repository"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/data/jpa 
        http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
        http://www.springframework.org/schema/data/repository
        http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd">

    <context:component-scan base-package="ar.com.boldt.godzilla" />
    <jpa:repositories base-package="xx.xxx.xxx.godzilla.business.dao" />

    <bean id="jpaVendorAdapter"
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="${dataSource.show.sql}" />
        <property name="generateDdl" value="false" />
        <property name="database" value="SQL_SERVER" />
    </bean>

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
        <!-- spring based scanning for entity classes -->
        <property name="packagesToScan" value="xx.xxx.xxx.godzilla.business.dao" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcache" />
    </bean>

    <bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml" />
    </bean>
</beans>

Et enfin une version édulcorée de la procédure stockée elle-même:

ALTER PROCEDURE [Terminal_GetTicket](
 @arg int 
,@res int output
,@res2 int output
)
as
Declare @error int

select 0, 1, 2

RETURN @error

Maintenant, chaque fois que j'essaie de définir le @Autowired annotation, je reçois l'exception mentionnée ci-dessus.

16
Rys

Je me souviens que j'avais du mal avec les procédures stockées MS SQL et spring-data-jpa. Voici comment j'ai réussi à l'exécuter:

Modèle:

@NamedNativeQueries({
    @NamedNativeQuery(
            name = "yourInternalName",
            query = "EXEC [procedure_name] :param1, :param2",
            resultClass = Foo.class
    )
 })
 @Entity
 public class Foo{

     /* Fields, getters, setters*/
}

C'est assez simple. Cette approche est cependant différente, vous ne déclarez pas de procédures directement (c'est aussi la raison pour laquelle cela ne doit pas fonctionner si vous décidez de changer RDBS).

Ensuite, vous devez étendre votre référentiel:

public interface FooRepositoryCustom {

     Foo fancyMethodName(arg1, arg2);
}

Et implémentez-le directement:

public class FooRepositoryImpl implements FooRepositoryCustom {


@PersistenceContext
EntityManager entityManager;

@Override
public Foo fancyMethodName(arg1, arg2) {

    Query query = entityManager.createNamedQuery("yourInternalName");
    query.setParameter("param1", arg1);
    query.setParameter("param2", arg2);
    return query.getResultList();
}

Mettons tout cela ensemble:

public interface FooRepository extends CrudRepository<Foo, Long>, FooRepositoryCustom {

}

Notez que si vous décidez de renvoyer par exemple une liste d'objets Foo, vous modifiez uniquement la valeur de retour dans votre référentiel personnalisé.

8
SirKometa

J'ai suivi les conseils de SirKometas mais je ne pouvais pas le faire fonctionner alors j'ai trouvé quelque chose qui fonctionnait pour moi et je pense que du point de vue de la syntaxe, c'est mieux. Créez d'abord votre classe d'entité comme ci-dessous.

@NamedStoredProcedureQueries({//
    @NamedStoredProcedureQuery(//
            name = "MySP"//
            , procedureName = "my_sp"//
            , parameters = { //
                    @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = String.class)}//
            , resultClasses = Foo.class)//})
@Entity
public class Foo {

La classe d'implémentation du référentiel serait alors:

@Component
public class FooRepositoryImpl implements FooCustomRepository {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public List<Foo> foo(String arg) {
        Query query = entityManager.createNamedStoredProcedureQuery("MySP");
        query.setParameter("arg", arg);
        return query.getResultList();
    }
}

Le reste de l'implémentation est comme la réponse de SirKometa ci-dessus. Pensez également que vous devez créer un bean EntityManager dans votre application pour que cela fonctionne.

5
Keyhan