2017-09-25 3 views
0

Je suis tout à fait newwbee avec spring-data-cassandra et je suis confronté à des problèmes lorsque je tente de créer une ligne dans une table de cassandra.À propos d'une exception org.springframework.core.convert.ConverterNotFoundException dans spring-data-cassandra quand j'essaie d'insérer une ligne

C'est l'exception lorsque je tente de lancer le test, méthode de configuration est jamais exécuté:

org.springframework.core.convert.ConversionFailedException: **Failed to convert from type [java.util.HashSet<?>] to type [java.lang.String] for value '[[email protected][** 
id=data_sync_id 
orgId=identifier 
tenantId=_tenand_id 
syncDateTime=2017-09-25T13:35:14.153 
syncType=all 
syncStatus=fully_completed 
]]'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [unicon.matthews.entity.DataSync] to type [java.lang.String] 

... 
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [unicon.matthews.entity.DataSync] to type [java.lang.String] 
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:324) 
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:206) 
at org.springframework.core.convert.support.CollectionToStringConverter.convert(CollectionToStringConverter.java:71) 
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:37) 
... 60 more 

C'est le test:

@RunWith(SpringRunner.class) 
@SpringBootTest(classes = unicon.matthews.oneroster.service.repository.CassandraConfiguration.class) 
public class CassandraOrgRepositoryTests { 

final String _userName = UUID.randomUUID().toString(); 
final String _orgName = UUID.randomUUID().toString(); 
final String _sourceId = UUID.randomUUID().toString(); 
final String _id = UUID.randomUUID().toString(); 
final String _api_key = UUID.randomUUID().toString(); 
final String _api_secret = UUID.randomUUID().toString(); 
final String _tenant_id = "_tenand_id"; 
final Status _status = Status.inactive; 

final OrgType _org_type = OrgType.school; 
final String _org_identifier = UUID.randomUUID().toString(); 
@ClassRule public final static CassandraKeyspace CASSANDRA_KEYSPACE = CassandraKeyspace.onLocalhost().atLeast(Version.parse("3.0")); 

@Autowired CassandraOrgRepository repository; 

@Before 
public void setUp() throws Exception { 

    repository.deleteAll(); 
    OrgCassandraTable aPojo = new OrgCassandraTable(); 
    aPojo.setTenantId(_tenant_id); 
    Org.Builder myOrgBuilder = Org.Builder.class.newInstance(); 
    Map<String, String> metadata = new TreeMap<String,String>(); 
    metadata.put("key","value"); 
    Org myOrgPojo = myOrgBuilder.withIdentifier("identifier") 
       .withDateLastModified(LocalDateTime.now()) 
       .withMetadata(metadata) 
       .withName(_orgName) 
       .withSourcedId(_sourceId) 
       .withStatus(_status) 
       .withType(_org_type) 
       .build(); 

    aPojo.setSourcedId(_sourceId); 
    // active 0, 
    // inactive 1, 
    // tobedeleted 2; 
    aPojo.setStatus("1"); 
    aPojo.setDateLastModified(LocalDateTime.now()); 
    aPojo.setName(_orgName); 
    aPojo.setType(_org_type.toString()); 
    aPojo.setIdentifier(_org_identifier); 
    aPojo.setTenantId(_tenant_id); 

    // THIS MUST BE THE PROBLEM! 
    Set<DataSync> _dataSyncSet = new HashSet<DataSync>(); 
    DataSync.Builder _dataSyncBuilder = DataSync.Builder.class.newInstance(); 
    DataSync new_data_sync=_dataSyncBuilder.withId("data_sync_id") 
        .withOrgId(myOrgPojo.getIdentifier()) 
        .withSyncDateTime(LocalDateTime.now()) 
        .withSyncStatus(DataSync.DataSyncStatus.fully_completed) 
        .withSyncType(DataSync.DataSyncType.all) 
        .withTenantId(_tenant_id) 
        .build(); 
    _dataSyncSet.add(new_data_sync); 
    aPojo.setDataSyncs(_dataSyncSet); 
    aPojo.setApiSecret(_api_secret); 
    aPojo.setApiKey(_api_key); 
    aPojo.setId(_id); 
    repository.save(aPojo); 
    assertTrue(repository.count() > 0); 
    System.out.println("Created a org with fake data..."); 
} 

@Test 
public void testFindbyId() { 
    Optional<WrapperOrg> loaded = repository.findById(_id); 
    Assert.assertNotNull(loaded); 
    Assert.assertEquals("something went wrong...",_id,loaded.get().getId()); 
} 

}

Ceci est le dépôt :

import java.util.Optional; 
import org.springframework.data.cassandra.repository.CassandraRepository; 
import org.springframework.data.cassandra.repository.Query; 

// this repo must implement something that paginates rows, because ALLOW FILTERING must not be used 
public interface CassandraOrgRepository extends CassandraRepository<OrgCassandraTable> { 

@Query("SELECT * FROM org WHERE id = ?0") 
Optional<WrapperOrg> findById(final String id); 
@Query("SELECT * FROM org WHERE api_key = ?0 AND api_secret = ?1 ALLOW FILTERING") 
Optional<WrapperOrg> findByApiKeyAndApiSecret(final String apiKey, final String apiSecret); 
@Query("SELECT * FROM org WHERE api_key = ?0 ALLOW FILTERING") 
Optional<WrapperOrg> findByApiKey(final String apiKey); 
} 

C'est la classe CassandraConfiguration que je mentionne dans la classe de test. Je soupçonne que je vais devoir faire quelque chose ici:

import java.util.ArrayList; 
import java.util.List; 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 
import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.data.cassandra.config.SchemaAction; 
import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration; 
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; 

@Configuration 
@EnableAutoConfiguration 
public class CassandraConfiguration { 

@Configuration 
@EnableCassandraRepositories 
static class CassandraConfig extends AbstractCassandraConfiguration { 

private static final String KEYSPACE = "example"; 

@Override 
public String getKeyspaceName() { 
    return KEYSPACE; 
} 

@Override 
public SchemaAction getSchemaAction() { 
    return SchemaAction.RECREATE_DROP_UNUSED; 
} 

protected List<CreateKeyspaceSpecification> getKeyspaceCreations() { 
    List<CreateKeyspaceSpecification> createKeyspaceSpecifications = new ArrayList<>(); 
    createKeyspaceSpecifications.add(getKeySpaceSpecification()); 
    return createKeyspaceSpecifications; 
} 

// Below method creates KEYSPACE if it doesnt exist. 
private CreateKeyspaceSpecification getKeySpaceSpecification() { 

    CreateKeyspaceSpecification pandaCoopKeyspace = new CreateKeyspaceSpecification(); 
    pandaCoopKeyspace.name(KEYSPACE); 
    pandaCoopKeyspace.ifNotExists(true) 
      .createKeyspace(); 
    return pandaCoopKeyspace; 
} 


@Override 
public String getContactPoints() { 
    return "localhost"; 
} 

@Override 
public String[] getEntityBasePackages() { 
    return new String[] {"unicon.matthews.oneroster.service.repository"}; 
} 
} 
} 

Ceci est la classe Entité pojo:

import java.io.Serializable; 
import java.time.LocalDateTime; 
import java.util.Map; 
import java.util.Set; 
import org.springframework.cassandra.core.PrimaryKeyType; 
import org.springframework.data.annotation.LastModifiedDate; 
import org.springframework.data.cassandra.mapping.CassandraType; 
import org.springframework.data.cassandra.mapping.Column; 
import org.springframework.data.cassandra.mapping.Indexed; 
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn; 
import org.springframework.data.cassandra.mapping.Table; 
import com.datastax.driver.core.DataType; 
import unicon.matthews.entity.DataSync; 
import unicon.matthews.oneroster.Org; 
import unicon.matthews.oneroster.OrgType; 
import unicon.matthews.oneroster.Status; 

@Table(value=OrgCassandraTable.tableName) 
public class OrgCassandraTable implements Serializable{ 

@org.springframework.data.annotation.Transient 
public static final String tableName = "org"; 

@PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.PARTITIONED) 
@CassandraType(type = DataType.Name.TEXT) 
@Column("id") 
private String id; 

@Indexed 
@CassandraType(type = DataType.Name.TEXT) 
@Column("tenant_id") 
private String tenantId; 

@Indexed 
@CassandraType(type = DataType.Name.TEXT) 
@Column("api_key") 
private String apiKey; 

@Indexed 
@CassandraType(type = DataType.Name.TEXT) 
@Column("api_secret") 
private String apiSecret; 

@Indexed 
@CassandraType(type = DataType.Name.TEXT) 
@Column("org_source_id") 
private String sourcedId; 

@CassandraType(type = DataType.Name.TEXT) 
@Column("org_status") 
private String status; 

@Column("org_metadata") 
private Map<String, String> metadata; 

@Column("org_dateLastModified") 
@LastModifiedDate 
private LocalDateTime dateLastModified; 

@Column("org_name") 
@CassandraType(type = DataType.Name.TEXT) 
private String name; 

// ojito que esto es un enum 
@Column("org_type") 
@CassandraType(type = DataType.Name.TEXT) 
private String type; 

@Column("org_identifier") 
@CassandraType(type = DataType.Name.TEXT) 
@Indexed 
private String identifier; 

// THIS FIELD LOOKS TO BE THE PROBLEM! 
@Column("org_data_syncs") 
@CassandraType(type = DataType.Name.TEXT) 
private Set<DataSync> dataSyncs; 

public OrgCassandraTable(){ 

} 

Cette classe est DataSync. Il appartient à une bibliothèque tierce, je n'ai pas le code. Qu'est-ce que je fais mal?

public class DataSync implements Serializable { 
private static final long serialVersionUID = 1L; 
private String id; 
private String orgId; 
private String tenantId; 
private LocalDateTime syncDateTime; 
private DataSync.DataSyncType syncType; 
private DataSync.DataSyncStatus syncStatus; 


...getters, setters, equals, hashCode, toString methods 
} 


... 

// getters, setters, hashCode, equals, toString methods. 
} 
+1

Comment voudriez-vous 'DataSync' pour être représenté dans un magasin de colonne? – mp911de

+0

Bonjour @ mp911de, merci pour la réponse. Je suppose que j'ai besoin chacun de tous les domaines appartenant à DataSync comme de nouvelles colonnes appartenant à la ligne. Dois-je mettre ces champs de DataSync dans la classe pojo OrgCassandraTable? – aironman

Répondre

1

Cassandra est un magasin en colonnes - données Spring Cassandra cartes chaque classe de domaine à une seule table, il n'y a pas de relations, et il n'y a pas (pas encore, mais pourrait venir) support pour les objets embarqués. Objets incorporés dans le sens d'aplatissement de la structure de données vers les colonnes de la table à laquelle l'objet englobant est mappé.

Cependant, il existe un support pour user-defined types via @UserDefinedType sur la classe d'objets représentant la structure de données. Ajouter @UserDefinedType nécessite d'avoir le contrôle sur la classe/le code.

Si vous voulez coller à la classe, alors vous avez encore une option pour sérialiser les données vous-même, par exemple, en utilisant Jackson et le stockage du JSON dans une seule colonne Cassandra:

static class DataSyncWriteConverter implements Converter<DataSync, String> { 

    public String convert(DataSync source) { 

    try { 
     return new ObjectMapper().writeValueAsString(source); 
    } catch (IOException e) { 
     throw new IllegalStateException(e); 
    } 
    } 
} 

Vous devriez pouvoir pour travailler avec des types de collection, ce qui signifie que vous pouvez conserver un Set<DataSync> dans une colonne set<varchar> dans Cassandra avec cette approche.

Une dernière chose: Utilisation des classes de 3e parti vient au risque de changements aux classes externes où vous n'avez pas le contrôle. Création d'une propre structure de données en reproduisant tous les champs et la cartographie des données au 3e parti de la classe vous permettent de contrôler le cycle de vie des changements.

Références: