2014-10-07 15 views
5

quindi il mio modello di database funziona così: ho Store s e ogni Store ha un nome localizzato. Così ho scelto per rappresentare il nome localizzato come Map come questo:Come richiedere i valori della mappa con Spring Data JPA?

public class Store { 
    private Map<Locale,LocalizedValue> name; 
} 

come si può vedere si tratta di una Mappa <Locale, LocalizedValue> dove il LocalizedValue è una classe come questa:

@Embeddable 
public class LocalizedValue { 

    @Column(name = "value") 
    private String value; 
} 

e tutto funziona alla grande. Tuttavia, ho un problema in cui voglio interrogare il mio repository JPA Spring Data e trovare tutti i negozi con un determinato nome inglese. Quindi il mio metodo repository è simile al seguente:

Store findByName(Map.Entry<Locale, LocalizedValue> name); 

ma genera questa eccezione:

2014-10-07 23:49:55,862 [qtp354231028-165] ERROR: Parameter value [en=Some Value] did not match expected type [com.test.LocalizedValue(n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [en=Some Value] did not match expected type [com.test.LocalizedValue (n/a)] 
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [en=Some Value] did not match expected type [com.test.LocalizedValue (n/a)]; 
nested exception is java.lang.IllegalArgumentException: Parameter value [en=Some Value] did not match expected type [com.test.LocalizedValue (n/a)] 
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384) 
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:216) 
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) 
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) 
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) 

Così poi ho cambiato il mio metodo repository per essere come questo:

Store findByName(LocalizedValue name); 

ma poi ho ha ottenuto questa eccezione:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'name1_.pk' in 'where clause' 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526) 

Ho anche provato a utilizzare Contiene nel metodo di query - ancora senza fortuna.

Quindi la mia domanda è: c'è un modo per interrogare i negozi con il nome inglese "Some Value"?

risposta

7

Questo richiede una query qualcosa di definito manualmente in questo modo:

interface StoreRepository extends Repository<Store, Long> { 

    @Query("select s from Store s join s.map m where ?1 in (VALUE(m))" 
    Optional<Store> findByName(String name); 
} 

Racconta fondamentalmente il provider di persistenza per espandere i valori della mappa e verificare se il parametro data è nella lista dei valori espanso.

+2

Grazie per la risposta. Questo è documentato in Spring JPA? Dove posso ottenere maggiori informazioni su come i valori della mappa vengono espansi da Spring JPA. – Sharadr

+0

Lo stesso può essere fatto con il parametro Collection? – mihn

1

Non hai pubblicato i tuoi mapping ma mi sembra che sia un problema fondamentale per il modo in cui sono incorporati gli Embeddable.

Questo è l'unico modo ovvio che posso vedere per mappare l'associazione:

@Entity 
@Table(name = "stores") 
public class Store { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "id") 
    private Long id; 

    @ElementCollection 
    @CollectionTable(name = "store_names", joinColumns = @JoinColumn(name = "store_id")) 
    @MapKeyColumn(name = "locale") 
    private Map<Locale, LocalizedName> names; 

    public Map<Locale, LocalizedName> getNames() { 
     return names; 
    } 

    public String getName(Locale locale) { 
     return names.get(locale).getName(); 
    } 
} 


@Embeddable 
public class LocalizedName { 

    @Column(name = "name") 
    private String name; 

    @SuppressWarnings("unused") 
    private LocalizedName() { 

    } 

    public LocalizedName(String name) { 
     this.name = name; 
    } 

    public String getName(){ 
     return name; 
    } 
} 

Il seguente test passa:

@Test 
public void testLoadStore() { 
    Store store = repository.findOne(1l); 
    Assert.assertNotNull(store); 
    Assert.assertEquals("EN Name", store.getName(Locale.ENGLISH)); 
    Assert.assertEquals("DE Name", store.getName(Locale.GERMAN)); 
} 

Il problema con questo è, tuttavia, che 'Locale' non può mai essere una proprietà di LocalisedName, altrimenti Hibernate si lamenta di una mappatura di colonne duplicate. Vedere la segnalazione di bug:

https://hibernate.atlassian.net/browse/HHH-5393

Così, mentre è possibile scrivere un metodo di query:

interfaccia pubblica StoreRepository estende JpaRepository {

@Query(value = "from Store s join s.names n where n.name = ?1") 
Store findByName(String name); 

}

per i quali il seguenti passaggi di prova

@Test 
public void testLoadByName() { 
    Store store = repository.findByName("EN Name"); 
    Assert.assertNotNull(store); 
    Assert.assertEquals("EN Name", store.getName(Locale.ENGLISH)); 
    Assert.assertEquals("DE Name", store.getName(Locale.GERMAN)); 
} 

per quanto posso vedere questo non può mai prendere in considerazione Locale in quanto non è una proprietà di LocalizedName.

+0

No, certo che non funzionerà, non si specifica mai l'impostazione internazionale. L'ho provato e ho ricevuto: "Il valore del parametro [Some Value] non corrispondeva al tipo previsto [com.test.LocalizedValue (n/a)];" –

+0

Vedere la risposta aggiornata. –

Problemi correlati