Integration

Builds tools

The java-driver-mapper-processor artifact contains the annotation processor. It hooks into the Java compiler, and generates additional source files from your annotated classes before the main compilation happens. It is only required in the compile classpath.

The java-driver-mapper-runtime artifact contains the annotations and a few utility classes. It is a regular dependency, required at runtime.

Maven

The best approach is to add the annotationProcessorPaths option to the compiler plugin’s configuration (make sure you use version 3.5 or higher):

<properties>
  <java-driver.version>...</java-driver.version>
</properties>

<dependencies>
  <dependency>
    <groupId>com.datastax.oss</groupId>
    <artifactId>java-driver-mapper-runtime</artifactId>
    <version>${java-driver.version}</version>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.1</version>
      <configuration>
        <source>1.8</source> <!-- (or higher) -->
        <target>1.8</target> <!-- (or higher) -->
        <annotationProcessorPaths>
          <path>
            <groupId>com.datastax.oss</groupId>
            <artifactId>java-driver-mapper-processor</artifactId>
            <version>${java-driver.version}</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

Alternatively (e.g. if you are using the BOM), you may also declare the processor as a regular dependency in the “provided” scope:

<dependencies>
  <dependency>
    <groupId>com.datastax.oss</groupId>
    <artifactId>java-driver-mapper-processor</artifactId>
    <version>${java-driver.version}</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>com.datastax.oss</groupId>
    <artifactId>java-driver-mapper-runtime</artifactId>
    <version>${java-driver.version}</version>
  </dependency>
</dependencies>

The processor runs every time you execute the mvn compile phase. It normally supports incremental builds, but if something looks off you can try a full rebuild with mvn clean compile.

One of the advantages of annotation processing is that the generated code is produced as regular source files, that you can read and debug like the rest of your application. With the above configuration, these files are in target/generated-sources/annotations. Make sure that directory is marked as a source folder in your IDE (for example, in IntelliJ IDEA, this might require right-clicking on your pom.xml and selecting “Maven > Reimport”).

Generated sources follow the same package structure as your annotated types. Most end in a special __MapperGenerated suffix, in order to clearly identify them in stack traces (one exception is the mapper builder, because it is referenced directly from your code).

Do not edit those files files directly: your changes would be overwritten during the next full rebuild.

Gradle

Use the following configuration (Gradle 4.6 and above):

apply plugin: 'java'

def javaDriverVersion = '...'

dependencies {
    annotationProcessor group: 'com.datastax.oss', name: 'java-driver-mapper-processor', version: javaDriverVersion
    compile group: 'com.datastax.oss', name: 'java-driver-mapper-runtime', version: javaDriverVersion
}

You will find the generated files in build/generated/sources/annotationProcessor.

Integration with other languages and libraries

Lombok

Lombok is a popular library that automates boilerplate code, such as getters and setters. This can be convenient for mapped entities:

import com.datastax.oss.driver.api.mapper.annotations.Entity;
import com.datastax.oss.driver.api.mapper.annotations.PartitionKey;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Entity
@EqualsAndHashCode
@ToString
public class Product {
  @PartitionKey @Getter @Setter private int id;
  @Getter @Setter private String description;
}

The mapper can process Lombok-annotated classes just like regular code. The only requirement is that Lombok’s annotation processor must run before the mapper’s.

With Maven, declaring Lombok as a provided dependency is not enough; you must also redeclare it in the <annotationProcessorPaths> section, before the mapper:

<properties>
  <java-driver.version>...</java-driver.version>
  <lombok.version>...</lombok.version>
</properties>

<dependencies>
  <dependency>
    <groupId>com.datastax.oss</groupId>
    <artifactId>java-driver-mapper-runtime</artifactId>
    <version>${java-driver.version}</version>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
          <path>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
          </path>
          <path>
            <groupId>com.datastax.oss</groupId>
            <artifactId>java-driver-mapper-processor</artifactId>
            <version>${java-driver.version}</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

With Gradle, a similar result can be achieved with:

apply plugin: 'java'

def javaDriverVersion = '...'
def lombokVersion = '...'

dependencies {
    annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombokVersion
    annotationProcessor group: 'com.datastax.oss', name: 'java-driver-mapper-processor', version: javaDriverVersion
    compile group: 'com.datastax.oss', name: 'java-driver-mapper-runtime', version: javaDriverVersion
    compileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion
}

You’ll also need to install a Lombok plugin in your IDE (for IntelliJ IDEA, this one is available in the marketplace).

Kotlin

Kotlin is an alternative language for the JVM. Its compact syntax and native support for annotation processing make it a good fit for the mapper.

To set up your project, refer to the Kotlin website:

  • Maven: configure dual compilation of Kotlin and Java sources. In addition, you’ll need an additional execution of the kotlin-maven-plugin:kapt goal with the mapper processor before compilation:

    <plugin>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-maven-plugin</artifactId>
      <version>${kotlin.version}</version>  
      <executions>  
        <execution>
          <id>kapt</id>
          <goals><goal>kapt</goal></goals>
          <configuration>
            <sourceDirs>
              <sourceDir>src/main/kotlin</sourceDir>
              <sourceDir>src/main/java</sourceDir>
            </sourceDirs>
            <annotationProcessorPaths>
              <annotationProcessorPath>
                <groupId>com.datastax.oss</groupId>
                <artifactId>java-driver-mapper-processor</artifactId>
                <version>${java-driver.version}</version>
              </annotationProcessorPath>
            </annotationProcessorPaths>
          </configuration>
        </execution>
        <execution>
          <id>compile</id>
          <goals><goal>compile</goal></goals>
          ...
        </execution>
      </executions>
    </plugin>
    
  • Gradle: configure the kotlin and kotlin_kapt plugins in your build script. In addition, declare the dependency to the mapper processor with kapt instead of annotationProcessor:

    apply plugin: 'kotlin'
    apply plugin: 'kotlin-kapt'
    
    dependencies {
        kapt group: 'com.datastax.oss', name: 'java-driver-mapper-processor', version: javaDriverVersion
        ...
    }
    

You can use Kotlin data classes for your entities. Just keep in mind that the mapper expects a no-arg constructor, which means that you must define default values; and setters, which means that properties must be declared with var, not val.

@Entity
data class Product(@PartitionKey var id: Int? = null, var description: String? = null)

All of the property annotations can be declared directly on the constructor properties.

If you want to take advantage of null saving strategies, your properties should be nullable.

The other mapper interfaces are pretty similar to the Java versions:

@Dao
interface ProductDao {
  @Insert
  fun insert(product: Product)
}