diff --git a/persistence-modules/spring-data-cassandra-2/pom.xml b/persistence-modules/spring-data-cassandra-2/pom.xml index 1e6412dc17..740c04d2a0 100644 --- a/persistence-modules/spring-data-cassandra-2/pom.xml +++ b/persistence-modules/spring-data-cassandra-2/pom.xml @@ -60,8 +60,32 @@ ${system.stubs.version} test + + com.datastax.oss + java-driver-mapper-runtime + 4.15.0 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + com.datastax.oss + java-driver-mapper-processor + 4.15.0 + + + + + + + 11 3.1.11 diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/CassandraMapperApplication.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/CassandraMapperApplication.java new file mode 100644 index 0000000000..da66ee1401 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/CassandraMapperApplication.java @@ -0,0 +1,12 @@ +package org.baeldung.objectmapper; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CassandraMapperApplication { + + public static void main(String[] args) { + SpringApplication.run(CassandraMapperApplication.class, args); + } +} diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/DaoMapper.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/DaoMapper.java new file mode 100644 index 0000000000..f7e937702d --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/DaoMapper.java @@ -0,0 +1,18 @@ +package org.baeldung.objectmapper; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.mapper.annotations.DaoFactory; +import com.datastax.oss.driver.api.mapper.annotations.DaoKeyspace; +import com.datastax.oss.driver.api.mapper.annotations.Mapper; +import org.baeldung.objectmapper.dao.CounterDao; +import org.baeldung.objectmapper.dao.UserDao; + +@Mapper +public interface DaoMapper { + + @DaoFactory + UserDao getUserDao(@DaoKeyspace CqlIdentifier keyspace); + + @DaoFactory + CounterDao getUserCounterDao(@DaoKeyspace CqlIdentifier keyspace); +} \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/CounterDao.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/CounterDao.java new file mode 100644 index 0000000000..71978ce116 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/CounterDao.java @@ -0,0 +1,16 @@ +package org.baeldung.objectmapper.dao; + +import com.datastax.oss.driver.api.mapper.annotations.Dao; +import com.datastax.oss.driver.api.mapper.annotations.Increment; +import com.datastax.oss.driver.api.mapper.annotations.Select; +import org.baeldung.objectmapper.entity.Counter; + +@Dao +public interface CounterDao { + + @Increment(entityClass = Counter.class) + void incrementCounter(String id, long count); + + @Select + Counter getCounterById(String id); +} diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/UserDao.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/UserDao.java new file mode 100644 index 0000000000..9e06066c1e --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/UserDao.java @@ -0,0 +1,38 @@ +package org.baeldung.objectmapper.dao; + +import com.datastax.oss.driver.api.core.PagingIterable; +import com.datastax.oss.driver.api.core.cql.BoundStatement; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.mapper.annotations.*; +import org.baeldung.objectmapper.entity.User; + +@Dao +public interface UserDao { + + @Insert + void insertUser(User user); + + @Select + User getUserById(int id); + + @Select + PagingIterable getAllUsers(); + + @Update + void updateUser(User user); + + @Delete + void deleteUser(User user); + + @GetEntity + User getUser(Row row); + + @SetEntity + BoundStatement setUser(BoundStatement udtValue, User user); + + @Query(value = "select * from user_profile where user_age > :userAge ALLOW FILTERING") + PagingIterable getUsersOlderThanAge(int userAge); + + @QueryProvider(providerClass = UserQueryProvider.class, entityHelpers = User.class, providerMethod = "getUsersOlderThanAge") + PagingIterable getUsersOlderThan(String age); +} diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/UserQueryProvider.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/UserQueryProvider.java new file mode 100644 index 0000000000..10c56a9310 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/dao/UserQueryProvider.java @@ -0,0 +1,34 @@ +package org.baeldung.objectmapper.dao; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.PagingIterable; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.mapper.MapperContext; +import com.datastax.oss.driver.api.mapper.entity.EntityHelper; +import com.datastax.oss.driver.api.querybuilder.QueryBuilder; +import org.baeldung.objectmapper.entity.User; + +public class UserQueryProvider { + + private final CqlSession session; + private final EntityHelper userHelper; + + public UserQueryProvider(MapperContext context, EntityHelper userHelper) { + this.session = context.getSession(); + this.userHelper = userHelper; + } + + public PagingIterable getUsersOlderThanAge(String age) { + SimpleStatement statement = QueryBuilder.selectFrom("user_profile") + .all() + .whereColumn("user_age") + .isGreaterThan(QueryBuilder + .bindMarker(age)) + .build(); + PreparedStatement preparedSelectUser = session.prepare(statement); + return session + .execute(preparedSelectUser.getQuery()) + .map(result -> userHelper.get(result, true)); + } +} diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/Admin.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/Admin.java new file mode 100644 index 0000000000..afd6b74490 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/Admin.java @@ -0,0 +1,31 @@ +package org.baeldung.objectmapper.entity; + +import com.datastax.oss.driver.api.mapper.annotations.CqlName; +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.HierarchyScanStrategy; + +@Entity +@CqlName("admin_profile") +@HierarchyScanStrategy(highestAncestor = User.class, includeHighestAncestor = true) +public class Admin extends User { + private String role; + private String department; + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + +} + diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/Counter.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/Counter.java new file mode 100644 index 0000000000..88b49b561e --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/Counter.java @@ -0,0 +1,29 @@ +package org.baeldung.objectmapper.entity; + +import com.datastax.oss.driver.api.mapper.annotations.Entity; +import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; + +@Entity +public class Counter { + + @PartitionKey + private String id; + private long count; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public long getCount() { + return count; + } + + public void setCount(long count) { + this.count = count; + } + +} diff --git a/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/User.java b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/User.java new file mode 100644 index 0000000000..31612ffe73 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/main/java/org/baeldung/objectmapper/entity/User.java @@ -0,0 +1,58 @@ +package org.baeldung.objectmapper.entity; + +import com.datastax.oss.driver.api.mapper.annotations.*; + +@Entity +@CqlName("user_profile") +public class User { + @PartitionKey + private int id; + @CqlName("username") + private String userName; + @ClusteringColumn + private int userAge; + + @Computed("writetime(userName)") + private long writetime; + + public User() { + } + + public User(int id, String userName, int userAge) { + this.id = id; + this.userName = userName; + this.userAge = userAge; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public int getUserAge() { + return userAge; + } + + public void setUserAge(int userAge) { + this.userAge = userAge; + } + + public long getWritetime() { + return writetime; + } + + public void setWritetime(long writetime) { + this.writetime = writetime; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-cassandra-2/src/test/java/org/baeldung/objectmapper/MapperLiveTest.java b/persistence-modules/spring-data-cassandra-2/src/test/java/org/baeldung/objectmapper/MapperLiveTest.java new file mode 100644 index 0000000000..b61663d622 --- /dev/null +++ b/persistence-modules/spring-data-cassandra-2/src/test/java/org/baeldung/objectmapper/MapperLiveTest.java @@ -0,0 +1,96 @@ +package org.baeldung.objectmapper; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import org.baeldung.objectmapper.dao.CounterDao; +import org.baeldung.objectmapper.dao.UserDao; +import org.baeldung.objectmapper.entity.Counter; +import org.baeldung.objectmapper.entity.User; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.testcontainers.containers.CassandraContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.List; + +@Testcontainers +@SpringBootTest +public class MapperLiveTest { + + private static final String KEYSPACE_NAME = "baeldung"; + + @Container + private static final CassandraContainer cassandra = (CassandraContainer) new CassandraContainer("cassandra:3.11.2") + .withExposedPorts(9042); + + static void setupCassandraConnectionProperties() { + System.setProperty("spring.data.cassandra.keyspace-name", KEYSPACE_NAME); + System.setProperty("spring.data.cassandra.contact-points", cassandra.getContainerIpAddress()); + System.setProperty("spring.data.cassandra.port", String.valueOf(cassandra.getMappedPort(9042))); + } + + static UserDao userDao; + static CounterDao counterDao; + + @BeforeAll + static void setup() { + setupCassandraConnectionProperties(); + CqlSession session = CqlSession.builder().build(); + + String createKeyspace = "CREATE KEYSPACE IF NOT EXISTS baeldung " + + "WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};"; + String useKeyspace = "USE baeldung;"; + String createUserTable = "CREATE TABLE IF NOT EXISTS user_profile " + + "(id int, username text, user_age int, writetime bigint, PRIMARY KEY (id, user_age)) " + + "WITH CLUSTERING ORDER BY (user_age DESC);"; + String createAdminTable = "CREATE TABLE IF NOT EXISTS admin_profile " + + "(id int, username text, user_age int, role text, writetime bigint, department text, " + + "PRIMARY KEY (id, user_age)) " + + "WITH CLUSTERING ORDER BY (user_age DESC);"; + String createCounter = "CREATE TABLE IF NOT EXISTS counter " + + "(id text, count counter, PRIMARY KEY (id));"; + + session.execute(createKeyspace); + session.execute(useKeyspace); + session.execute(createUserTable); + session.execute(createAdminTable); + session.execute(createCounter); + + DaoMapper mapper = new DaoMapperBuilder(session).build(); + userDao = mapper.getUserDao(CqlIdentifier.fromCql("baeldung")); + counterDao = mapper.getUserCounterDao(CqlIdentifier.fromCql("baeldung")); + } + + @Test + void givenUser_whenInsert_thenRetrievedDuringGet() { + User user = new User(1, "JohnDoe", 31); + userDao.insertUser(user); + User retrievedUser = userDao.getUserById(1); + Assertions.assertEquals(retrievedUser.getUserName(), user.getUserName()); + } + + @Test + void givenCounter_whenIncrement_thenIncremented() { + Counter users = counterDao.getCounterById("users"); + long initialCount = users != null ? users.getCount(): 0; + + counterDao.incrementCounter("users", 1); + + users = counterDao.getCounterById("users"); + long finalCount = users != null ? users.getCount(): 0; + + Assertions.assertEquals(finalCount - initialCount, 1); + } + + @Test + void givenUser_whenGetUsersOlderThan_thenRetrieved() { + User user = new User(2, "JaneDoe", 20); + userDao.insertUser(user); + List retrievedUsers = userDao.getUsersOlderThanAge(30).all(); + Assertions.assertEquals(retrievedUsers.size(), 1); + } + +} \ No newline at end of file