Run Query against multiple namespaces with Spring Data Cassandra - cassandra

Is there any way in which using Spring Data a query can be executed on all keyspaces in Cassandra?

There are two parts to this answer:
When using Spring Data Cassandra 1.x, you are need to setup individual CassandraTemplate instances for each keyspace you want to use.
With Spring Data Cassandra 2.x, we introduced the SessionFactory interface to control which Session to use. We ship with routing SessionFactory support so you can provide multiple sessions and a discriminator (usually something ThreadLocal-based) to select the appropriate Session.
Some example code for 2.0 would look like:
class MyRoutingSessionFactory extends AbstractRoutingSessionFactory {
ThreadLocal<String> lookupKey = ThreadLocal.withInitial(() -> "default-session");
void setLookupKey(String lookupKey) {
this.lookupKey.set(lookupKey);
}
#Override
protected Object determineCurrentLookupKey() {
return lookupKey.get();
}
}
class MyConfig extends AbstractCassandraConfiguration {
#Bean
#Override
public SessionFactory sessionFactory() {
MyRoutingSessionFactory factory = new MyRoutingSessionFactory();
factory.setDefaultTargetSessionFactory(getRequiredSession());
MapSessionFactoryLookup lookup = new MapSessionFactoryLookup();
Session myOtherSession = …;
lookup.addSessionFactory("default-session", getRequiredSession());
lookup.addSessionFactory("my-other-session", myOtherSession);
factory.setSessionFactoryLookup(lookup);
return factory;
}
// …
}

Related

Spring Integration JdbcMessageStore casting error

I am trying to create service that will read some data from remote server and process them using Spring Integration.
I have class that extends ArrayList, because I need to keep pointer to other page, so I can read it in next remote call. I set up release strategy to collect all these pages, until there is no pointer for the next page.
Here is definition of class:
public class CustomList extends ArrayList<DataInfo>
{
private String nextCursor;
// Methods omitted for readability
}
Everything worked fine, until I setup JdbcMessageStore in Aggregator, so I can keep messages in case of service shutdown.
Problem on which I come across is that in my release strategy class I cast my list class to same class (because message group does not define type), this exceptions is raised:
java.lang.ClassCastException: com.example.CustomList cannot be cast to com.example.CustomList
This is my release strategy class:
#Component
public class CursorReleaseStrategy implements ReleaseStrategy
{
#Override
public boolean canRelease(MessageGroup group)
{
return group.getMessages().stream()
.anyMatch(message -> ((CustomList) message.getPayload()).getNextCursor() == null);
}
}
If I remove message store, everything works fine, but the problem is that I need message store.
I am using spring boot 2.1.6 and Spring Integration DSL for creating this flow.
From what I read, this error happens because of different class loaders, but this I do from the same application.
Is there anything more that I need to configure for this to work_
Almost certainly a class loader issue; you can find which class loader loads each component (message store, release strategy) by injecting them into a bean and calling getClass().getClassLoader().
When application has been packaged in jar, there was such error.
So to fix the problem I created two beans, depending on profile.
For example:
#Profile("!prod")
#Bean
public MessageGroupStore messageStore(DataSource dataSource)
{
JdbcMessageStore jdbcMessageStore = new JdbcMessageStore(dataSource);
jdbcMessageStore.setDeserializer(inputStream -> {
ConfigurableObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, Thread.currentThread().getContextClassLoader());
try {
return (Message<?>) objectInputStream.readObject();
} catch (ClassNotFoundException var4) {
throw new NestedIOException("Failed to deserialize object type", var4);
}
});
return jdbcMessageStore;
}
#Profile("prod")
#Bean
public MessageGroupStore prodMessageStore(DataSource dataSource)
{
return new JdbcMessageStore(dataSource);
}

How to convert the cassandra row to my java class?

Here is [a Link](http://stackoverflow.com/questions/32448987/how-to-retrieve-a-very-big-cassandra-table-and-delete-some-unuse-data-from-it#comment52844466_32464409) of my question before.
After I get the cassandra data row by row in my program, I'm confused by the convert between cassandra row to java class. In java class the table of cassandra is convert to a ResultSet class,when I iterator it and get the row data,it returns a NPE. In fact,I can see the Object (or the data) while debuging the program. Here is My Iterator Code:
ResultSet rs=CassandraTools.getInstance().execute(cql);
Iterator<Row> iterator = rs.iterator();
while (iterator.hasNext()) {
Row row = iterator.next();
row.getString() ---->return NPE
The CassandraTools class is:
public class CassandraTools {
private static CassandraTools instance;
private CassandraTools() {
}
public static synchronized CassandraTools getInstance() {
if (instance == null) {
instance = new CassandraTools();
instance.init();
}
return instance;
}
Cluster cluster;
Session session;
public void init() {
if (cluster == null) {
cluster = new Cluster.Builder().addContactPoint("10.16.34.96")
.build();
if (session == null) {
session = cluster.connect("uc_passport");
}
}
}
public ResultSet execute(String cql) {
ResultSet rs = session.execute(cql);
// rs.forEach(n -> {
// System.out.println(n);
// });
return rs;
}
}
SO how could I convert the data in the row to A java Class?I have read the convert class in the API of spring data cassandra,but it is complicated to use for me. Who can help?
IMHO, If you want to map the rows of Cassandra to a java class, you should try to use an Object-Datastore mapper which does these things for you.
If you try to do this by yourself, you need to handle the java-cassandra datatype mappings, validations etc all by yourself which is very hectic job.
There are few (Kundera, Hibernate OGM, etc) opensource object-datastore mappers available and you can use them. I suggest you to try Kundera and check this for getting started with Cassandra.

i want to launch a class method periodically using spring

i have the following code.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(10000);
executor.setThreadNamePrefix("Executor-");
executor.initialize();
return executor;
}
}
and if i want to run the recommend method after every certain interval of time. What can be the java spring bean configuration way to do that.?
public class UserBrandsRecommender {
public List<RecommendedItem> recommend(Long userId, int number) throws TasteException{
}
}
You should look into the #Scheduled annotation. For example:
#Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
You'll probably need to create a new Spring bean with a method similar to above. The bean could have the UserBrandsRecommender injected into it. The new bean will need to implement some logic to pass proper values for the "userId" and "number" parameters to the "recommend" method.
More information here:
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/htmlsingle/#scheduling-annotation-support

How to use MyBatis with CDI

I am writing a web application and I am using MyBatis framework for persistence. I want to use CDI to inject the mappers easily and to manage the transaction declaratively.
MyBatis has official CDI support:
Mapper declaration:
#Mapper
public interface UserMapper {
#Select("SELECT * FROM users WHERE id = #{userId}")
User getUser(#Param("userId") String userId);
}
Mapper Usage:
public class MyService {
#Inject UserMapper userMapper;
public User doSomeStuff(String userId) {
return this.userMapper.getUser(userId);
}
}
More information in docs: http://mybatis.org/cdi/

Add behavior to existing implementation - C# / Design Pattern

My current implementation for service and business layer is straight forward as below.
public class MyEntity { }
// Business layer
public interface IBusiness { IList<MyEntity> GetEntities(); }
public class MyBusinessOne : IBusiness
{
public IList<MyEntity> GetEntities()
{
return new List<MyEntity>();
}
}
//factory
public static class Factory
{
public static T Create<T>() where T : class
{
return new MyBusinessOne() as T; // returns instance based on T
}
}
//Service layer
public class MyService
{
public IList<MyEntity> GetEntities()
{
return Factory.Create<IBusiness>().GetEntities();
}
}
We needed some changes in current implementation. Reason being data grew over the time and service & client cannot handle the volume of data. we needed to implement pagination to the current service. We also expect some more features (like return fault when data is more that threshold, apply filters etc), so the design needs to be updated.
Following is my new proposal.
public interface IBusiness
{
IList<MyEntity> GetEntities();
}
public interface IBehavior
{
IEnumerable<T> Apply<T>(IEnumerable<T> data);
}
public abstract class MyBusiness
{
protected List<IBehavior> Behaviors = new List<IBehavior>();
public void AddBehavior(IBehavior behavior)
{
Behaviors.Add(behavior);
}
}
public class PaginationBehavior : IBehavior
{
public int PageSize = 10;
public int PageNumber = 2;
public IEnumerable<T> Apply<T>(IEnumerable<T> data)
{
//apply behavior here
return data
.Skip(PageNumber * PageSize)
.Take(PageSize);
}
}
public class MyEntity { }
public class MyBusinessOne : MyBusiness, IBusiness
{
public IList<MyEntity> GetEntities()
{
IEnumerable<MyEntity> result = new List<MyEntity>();
this.Behaviors.ForEach(rs =>
{
result = rs.Apply<MyEntity>(result);
});
return result.ToList();
}
}
public static class Factory
{
public static T Create<T>(List<IBehavior> behaviors) where T : class
{
// returns instance based on T
var instance = new MyBusinessOne();
behaviors.ForEach(rs => instance.AddBehavior(rs));
return instance as T;
}
}
public class MyService
{
public IList<MyEntity> GetEntities(int currentPage)
{
List<IBehavior> behaviors = new List<IBehavior>() {
new PaginationBehavior() { PageNumber = currentPage, }
};
return Factory.Create<IBusiness>(behaviors).GetEntities();
}
}
Experts please suggest me if my implementation is correct or I am over killing it. If it correct what design pattern it is - Decorator or Visitor.
Also my service returns JSON string. How can I use this behavior collections to serialize only selected properties rather than entire entity. List of properties comes from user as request. (Kind of column picker)
Looks like I don't have enough points to comment on your question. So, I am gonna make some assumption as I am not a C# expert.
Assumption 1: Looks like you are getting the data first and then applying the pagination using behavior object. If so, this is a wrong approach. Lets say there are 500 records and you are showing 50 records per fetch. Instead of simply fetching 50 records from DB, you are fetching 500 records for 10 times and on top of it you are adding a costly filter. DB is better equipped to do this job that C# or Java.
I would not consider pagination as a behavior with respect to the service. Its the behavior of the presentation layer. Your service should only worry about 'Data Granularity'. Looks like one of your customer wants all the data in one go and others might want a subset of that data.
Option 1: In DAO layer, have two methods: one for pagination and other for regular fetch. Based on the incoming params decide which method to call.
Option 2: Create two methods at service level. One for a small subset of data and the other for the whole set of data. Since you said JSON, this should be Restful service. Then based on the incoming URL, properly call the correct method. If you use Jersey, this should be easy.
In a service, new behaviors can be added by simply exposing new methods or adding new params to existing methods/functionalities (just make sure those changes are backward compatible). We really don't need Decorator or Visitor pattern. The only concern is no existing user should be affected.

Resources