Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UserTypeResolver must not be null

I have been attempting to test out an insert of a Cassandra UDT, and i keep running into the following error: Exception in thread "main" java.lang.IllegalArgumentException: UserTypeResolver must not be null

After just trying to figure my own way through it, i attempted to exactly replicate the approach outlined in the following: User Defined Type with spring-data-cassandra

However, i still get the same error.

I am able to insert to the target DB when i remove the UDT and just insert the simple types, so I know that I am connecting appropriately. My config is as follows:

@Configuration
@PropertySource(value = { "classpath:cassandra.properties" })
//@EnableCassandraRepositories(basePackages = { "org.spring.cassandra.example.repo" })
public class CassandraConfig {

private static final Logger LOG = LoggerFactory.getLogger(CassandraConfig.class);

@Autowired
private Environment env;

@Bean
public CassandraClusterFactoryBean cluster() {

    CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
    cluster.setContactPoints(env.getProperty("cassandra.contactpoints"));
    cluster.setPort(Integer.parseInt(env.getProperty("cassandra.port")));

    return cluster;
}

@Bean
public CassandraMappingContext mappingContext() {
    BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
    mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), "campaign_management"));
    return mappingContext;
}

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

@Bean
public CassandraSessionFactoryBean session() throws Exception {

    CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
    session.setCluster(cluster().getObject());
    session.setKeyspaceName(env.getProperty("cassandra.keyspace"));
    session.setConverter(converter());
    session.setSchemaAction(SchemaAction.NONE);

    return session;
}

@Bean
public CassandraOperations cassandraTemplate() throws Exception {
    return new CassandraTemplate(session().getObject());
}
}

My Address and Employee classes are exactly as shown in the SO question i reference above, and my Main is simply:

public class MainClass {

public static void main(String[] args) {

ApplicationContext service = new AnnotationConfigApplicationContext(CassandraConfig.class);

Employee employee = new Employee();
employee.setEmployee_id(UUID.randomUUID());
employee.setEmployee_name("Todd");
Address address = new Address();
address.setAddress_type("Home");
address.setId("ToddId");
employee.setAddress(address);
CassandraOperations operations = service.getBean("cassandraTemplate", CassandraOperations.class);

operations.insert(employee);

System.out.println("Done");
}
}

I am using:

datastax.cassandra.driver.version=3.1.3
spring.data.cassandra.version=1.5.1
spring.data.commons.version=1.13.1
spring.cql.version=1.5.1

The version referenced in the previous SO question is 1.5.0, though spring.io lists 1.5.1 as current, so I am using that, and no 1.5.0 is shown available.

Any help would be appreciated, as this is driving me somewhat nuts.

like image 353
TBlank Avatar asked Apr 09 '17 18:04

TBlank


1 Answers

You typically get this error when you miss a UserTypeResolver under your cassandra Mapping, itself used by the cassandra Converter, itself used by the Spring Data Cassandra Template

For the details:

Assuming you have a basic Spring MVC Controller up and running elsewhere...

UserDefinedTypes in Cassandra being most interesting within SETs and MAPs, the example below is of such kind.

Example Spring Bean configuration with all defaults (Spring XML application context extract):

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:task="http://www.springframework.org/schema/task"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
   http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
...
<!-- ===== CASSANDRA ===== -->
<!-- Loads the properties into the Spring Context and uses them to fill in placeholders in bean definitions below -->
<context:property-placeholder location="/WEB-INF/spring/cassandra.properties" />

<!-- REQUIRED: The Cassandra Cluster -->
<cassandra:cluster contact-points="${cassandra.contactpoints}"
    port="${cassandra.port}" username="cassandra" password="cassandra"
    auth-info-provider-ref="authProvider" />

<!-- REQUIRED: The Cassandra Session, built from the Cluster, and attaching to a keyspace -->
<cassandra:session keyspace-name="${cassandra.keyspace}" />

<!-- REQUIRED: The Default Cassandra Mapping Context used by CassandraConverter 
     DO include a userTypeResolver for UDT support -->
<cassandra:mapping entity-base-packages="fr.woobe.model">
    <cassandra:user-type-resolver keyspace-name="${cassandra.keyspace}" />
</cassandra:mapping>

<!-- REQUIRED: The Default Cassandra Converter used by CassandraTemplate -->
<cassandra:converter />

<bean id="authProvider" class="com.datastax.driver.core.PlainTextAuthProvider">
    <constructor-arg index="0" value="myCassandraUser" />
    <constructor-arg index="1" value="somePassword" />
</bean>

<!-- REQUIRED: The Cassandra Template is the building block of all Spring Data Cassandra -->
<cassandra:template id="cassandraTemplate" />
...

and then in java, typically within your Spring MVC controller:

import org.springframework.data.cassandra.core.CassandraOperations;
...
// acquire DB template
CassandraOperations cOps = this.beanFactory.getBean("cassandraTemplate", CassandraOperations.class);
// for instance: load everything
List<MyData> rows = cOps.select("SELECT * FROM mydatatable", MyData.class);
// assuming an entry with index i exists...
Set<Pair> mySetOfPairs = rows.get(i).pairSet;
if (mySetOfPairs!=null)
   for (Pair p : mySetOfPairs) { 
        ... handle p.first and p.second ...
...

with this kind of entity mappings:

package example.model;
import java.util.Set;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
import com.datastax.driver.core.DataType.Name;

@Table public class MyData {
   @PrimaryKey 
   public String myKey;
   // some other basic fields...
   public String moreStuff;
   // a SET of user defined 'pair type'
   @CassandraType(type = Name.SET, userTypeName = "pairType")
   public Set<Pair> pairSet;

   // your constructors and other methods here...
}

and a user defined entity like:

package example.model;
import org.springframework.data.cassandra.core.mapping.UserDefinedType;

@UserDefinedType("pairType")
public class Pair {
   public String first;
   public String second;

   public Pair() {
   }
   public Pair(String f, String s) {
    this.first= f;
    this.second= s;
   }
}

all based on a Cassandra table created as:

CREATE TYPE pairType (first text, second text);

CREATE TABLE MyData (
  myKey text,
  moreStuff text,
  pairSet set<frozen<pairType>>,
  PRIMARY KEY (myKey)
  ) ;

INSERT INTO MyData (myKey, moreStuff, pairSet) 
  VALUES ('hello', 'world', { 
                  { first:'one', second:'two' }, 
                  { first:'out', second:'there' } } 
         ) ;

In term of Maven artifacts or libraries, spring-webmvc is indeed required if you run within a Web MVC Spring Controller, and then spring-context-support, and spring-data-cassandra. The DataStax cassandra driver comes along as a dependency.

like image 141
berhauz Avatar answered Sep 20 '22 09:09

berhauz