UserDefinedType not found by spring-data-cassandra

2020-07-30 04:29发布

问题:

based on the tutorial spring-data-cassandra-tutorial I'm experimenting with the UserDefinedType annotation.

I upgraded the project to use spring-data-cassandra:1.5.0.BUILD-SNAPSHOT, added the attribute price to the book, implemented the corresponding UserDefinedType and added setInitialEntitySet and setUserTypeResolver to the configuration.

When I then execute the BookRepositoryIntegrationTest against a cassandra 3.9, I get the following error:

org.springframework.data.mapping.model.MappingException: User type [price] not found

Any idea what I'm missing?

Here my changes...

Book.java:

package org.baeldung.spring.data.cassandra.model;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;

@Table
public class Book {

    @PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
    private UUID id;

    @PrimaryKeyColumn(name = "title", ordinal = 1, type = PrimaryKeyType.PARTITIONED)
    private String title;

    @PrimaryKeyColumn(name = "publisher", ordinal = 2, type = PrimaryKeyType.PARTITIONED)
    private String publisher;

    @Column
    private Set<String> tags = new HashSet<>();

    private Price price;

    public Book(final UUID id, final String title, final String publisher, final Set<String> tags) {
        this.id = id;
        this.title = title;
        this.publisher = publisher;
        this.tags.addAll(tags);
    }
}

pom.xml:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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>com.baeldung</groupId>
    <artifactId>spring-data-cassandra</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-data-cassandra</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <org.springframework.version>4.3.4.RELEASE</org.springframework.version>

        <org.springframework.data.version>1.5.0.BUILD-SNAPSHOT</org.springframework.data.version>

        <junit.version>4.12</junit.version>
        <org.slf4j.version>1.7.12</org.slf4j.version>
        <logback.version>1.1.3</logback.version>
        <cassandra-driver-core.version>3.1.2</cassandra-driver-core.version>
        <cassandra-unit.version>3.0.0.1</cassandra-unit.version>
        <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>        
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-cassandra</artifactId>
            <version>${org.springframework.data.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.cassandraunit</groupId>
            <artifactId>cassandra-unit-spring</artifactId>
            <version>${cassandra-unit.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.cassandraunit</groupId>
                    <artifactId>cassandra-unit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.cassandraunit</groupId>
            <artifactId>cassandra-unit</artifactId>
            <version>${cassandra-unit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.datastax.cassandra</groupId>
            <artifactId>cassandra-driver-core</artifactId>
            <version>${cassandra-driver-core.version}</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
                     <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>${maven-surefire-plugin.version}</version>
                        <configuration>
                            <excludes>
                                <exclude>**/*IntegrationTest.java</exclude>
                                <exclude>**/*LiveTest.java</exclude>
                            </excludes>
                        </configuration>
                    </plugin>
                </plugins>
            </build>

    <profiles>
        <profile>
            <id>integration</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <executions>
                            <execution>
                                <phase>integration-test</phase>
                                <goals>
                                    <goal>test</goal>
                                </goals>
                                <configuration>
                                    <excludes>
                                        <exclude>**/*LiveTest.java</exclude>
                                    </excludes>
                                    <includes>
                                        <include>**/*IntegrationTest.java</include>
                                    </includes>
                                </configuration>
                            </execution>
                        </executions>
                        <configuration>
                            <systemPropertyVariables>
                                <test.mime>json</test.mime>
                            </systemPropertyVariables>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

CassandraConfig.java

package org.baeldung.spring.data.cassandra.config;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.baeldung.spring.data.cassandra.model.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.cassandra.config.CassandraClusterFactoryBean;
import org.springframework.data.cassandra.config.CassandraEntityClassScanner;
import org.springframework.data.cassandra.config.java.AbstractCassandraConfiguration;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.SimpleUserTypeResolver;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;

@Configuration
@PropertySource(value = { "classpath:cassandra.properties" })
@EnableCassandraRepositories(basePackages = "org.baeldung.spring.data.cassandra.repository")
public class CassandraConfig extends AbstractCassandraConfiguration {
    private static final Log LOGGER = LogFactory.getLog(CassandraConfig.class);

    @Autowired
    private Environment environment;

    @Override
    protected String getKeyspaceName() {
        return environment.getProperty("cassandra.keyspace");
    }

    @Override
    @Bean
    public CassandraClusterFactoryBean cluster() {
        final CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
        cluster.setContactPoints(environment.getProperty("cassandra.contactpoints"));
        cluster.setPort(Integer.parseInt(environment.getProperty("cassandra.port")));
        LOGGER.info("Cluster created with contact points [" + environment.getProperty("cassandra.contactpoints") + "] " + "& port [" + Integer.parseInt(environment.getProperty("cassandra.port")) + "].");
        return cluster;
    }

    @Override
    @Bean
    public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
        BasicCassandraMappingContext ctx = new BasicCassandraMappingContext();
        ctx.setInitialEntitySet(CassandraEntityClassScanner.scan(Book.class));
        ctx.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), getKeyspaceName()));
        return ctx;
    }
}

Price.java:

package org.baeldung.spring.data.cassandra.model;

import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.UserDefinedType;

@UserDefinedType
public class Price {

    @Column
    private Double amount;

    @Column
    private String currency;

    public Price(Double amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Double getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }
}

回答1:

Try using below CassandraConfig class.

@Configuration
@PropertySource(value = { "classpath:cassandra.properties" })
@EnableCassandraRepositories(basePackages = "org.baeldung.spring.data.cassandra.repository")
public class CassandraConfig extends AbstractCassandraConfiguration {
private static final Log LOGGER = LogFactory.getLog(CassandraConfig.class);

@Autowired
private Environment environment;

@Override
protected String getKeyspaceName() {
    return environment.getProperty("cassandra.keyspace");
}

@Override
@Bean
public CassandraClusterFactoryBean cluster() {
    final CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
    cluster.setContactPoints(environment.getProperty("cassandra.contactpoints"));
    cluster.setPort(Integer.parseInt(environment.getProperty("cassandra.port")));
    LOGGER.info("Cluster created with contact points [" + environment.getProperty("cassandra.contactpoints") + "] " + "& port [" + Integer.parseInt(environment.getProperty("cassandra.port")) + "].");
    return cluster;
}


@Bean
public CassandraMappingContext mappingContext() throws ClassNotFoundException {
    BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
    mappingContext.setInitialEntitySet(CassandraEntityClassScanner.scan("org.baeldung.spring.data.cassandra.model"));
    mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(),getKeyspaceName()));
    return mappingContext;
}

@Bean
public CassandraConverter converter() throws ClassNotFoundException {
    return new MappingCassandraConverter(mappingContext());
}

@Bean
public CassandraSessionFactoryBean session() throws ClassNotFoundException {
    CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
    session.setCluster(cluster().getObject());
    session.setKeyspaceName(getKeyspaceName());
    session.setConverter(converter());
    session.setSchemaAction(SchemaAction.RECREATE);
    return session;
  }
}


回答2:

Your code looks good but misses user type creation. You also configured SimpleUserTypeResolver that is required to look up user types.

User type [price] not found means that the user type was not found in Cassandra.

You have two options how to fix that:

  1. You create the User-Defined type yourself.
  2. Use SchemaAction.RECREATE when bootstrapping the application.

Baeldung's tutorial uses table creation within the test so manual user type creation before table creation might be the better option.

CreateUserTypeSpecification userTypeSpecification = mappingContext.
                             getCreateUserTypeSpecificationFor(mappingContext.getExistingPersistentEntity(Price.class))
                            .ifNotExists(ifNotExists);

cqlTemplate.execute(CreateUserTypeCqlGenerator.toCql(userTypeSpecification));