关于 spring-data-cassandra 中的异常 org.springframework.core.convert.ConverterNotFoundException 当我尝试插入一行时
About an exception org.springframework.core.convert.ConverterNotFoundException in spring-data-cassandra when i try to insert a row
我是 spring-data-cassandra 的新手,当我尝试在 cassandra 中创建一行时遇到问题 table。
这是我尝试 运行 测试时的异常,setUp 方法从未执行过:
org.springframework.core.convert.ConversionFailedException: **Failed to convert from type [java.util.HashSet<?>] to type [java.lang.String] for value '[unicon.matthews.entity.DataSync@79135a38[**
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
这是测试:
@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());
}
}
这是存储库:
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);
}
这是我在测试class中提到的CassandraConfigurationclass。我怀疑我必须在这里做点什么:
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"};
}
}
}
这是实体 pojo class:
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(){
}
这是 DataSync class。它属于第三方库,我没有代码。我做错了什么?
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.
}
Cassandra 是面向列的存储 – Spring Data Cassandra 将每个域 class 映射到单个 table,没有关系,也没有(还没有,但可能会来)对嵌入式对象的支持。在将数据结构展平到 table 封闭对象映射到的列的意义上的嵌入式对象。
但是,表示数据结构的对象 class 通过 @UserDefinedType
支持 user-defined types。添加 @UserDefinedType
需要控制 class/code.
如果您想坚持使用 class,那么您仍然可以选择自己序列化数据,例如,使用 Jackson 并将 JSON 存储在单个 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);
}
}
}
您也应该能够使用集合类型,也就是说,您可以使用这种方法在 Cassandra 的 set<varchar>
列中保留 Set<DataSync>
。
最后一件事:使用第 3 方 classes 会面临更改您无法控制的外部 classes 的风险。通过复制所有字段并将数据映射到第 3 方来创建自己的数据结构-class 让您可以控制更改的生命周期。
参考文献:
我是 spring-data-cassandra 的新手,当我尝试在 cassandra 中创建一行时遇到问题 table。
这是我尝试 运行 测试时的异常,setUp 方法从未执行过:
org.springframework.core.convert.ConversionFailedException: **Failed to convert from type [java.util.HashSet<?>] to type [java.lang.String] for value '[unicon.matthews.entity.DataSync@79135a38[**
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
这是测试:
@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());
}
}
这是存储库:
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);
}
这是我在测试class中提到的CassandraConfigurationclass。我怀疑我必须在这里做点什么:
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"};
}
}
}
这是实体 pojo class:
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(){
}
这是 DataSync class。它属于第三方库,我没有代码。我做错了什么?
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.
}
Cassandra 是面向列的存储 – Spring Data Cassandra 将每个域 class 映射到单个 table,没有关系,也没有(还没有,但可能会来)对嵌入式对象的支持。在将数据结构展平到 table 封闭对象映射到的列的意义上的嵌入式对象。
但是,表示数据结构的对象 class 通过 @UserDefinedType
支持 user-defined types。添加 @UserDefinedType
需要控制 class/code.
如果您想坚持使用 class,那么您仍然可以选择自己序列化数据,例如,使用 Jackson 并将 JSON 存储在单个 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);
}
}
}
您也应该能够使用集合类型,也就是说,您可以使用这种方法在 Cassandra 的 set<varchar>
列中保留 Set<DataSync>
。
最后一件事:使用第 3 方 classes 会面临更改您无法控制的外部 classes 的风险。通过复制所有字段并将数据映射到第 3 方来创建自己的数据结构-class 让您可以控制更改的生命周期。
参考文献: