Saving an Iterable of a Spring Data Cassandra entity yields exception - spring-data

I have a very simple Groovy entity annotated with Spring Data Cassandra annotations. I've written a repo that is based on the CrudRepository interface. In my unit test, I'm testing the various methods in the repo, using Cassandra Unit and Spring Test. All the other methods in the interface that I'm testing seem to work fine. I can insert, delete, search etc from my application using the annotated entity. But when I try to save an Iterable of the entity through the repo, it fails with the following exception:
org.springframework.data.cassandra.mapping.VerifierMappingExceptions: java.util.ArrayList:
Cassandra entities must have the #Table, #Persistent or #PrimaryKeyClass Annotation
at org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntityMetadataVerifier.verify(BasicCassandraPersistentEntityMetadataVerifier.java:45)
at org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntity.verify(BasicCassandraPersistentEntity.java:198)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:317)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:179)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
at org.springframework.data.cassandra.core.CassandraTemplate.getTableNamndraTemplate.java:217)
at org.springframework.data.cassandra.core.CassandraTemplate.doInsert(CassandraTemplate.java:641)
at org.springframework.data.cassandra.core.CassandraTemplate.insert(CassandraTemplate.java:237)
at org.springframework.data.cassandra.core.CassandraTemplate.insert(CassandraTemplate.java:232)
at x.y.z.repositories.UserRepositoryImpl.save(UserRepositoryImpl.java:44)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:718)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at x.y.z.repositories.UserDataRepositorySpec.verify saving a list(UserDataRepositorySpec.groovy:186)
So, saving, deleting, or modifying a single entity instance works fine, but as soon as I create a List (backed by an ArrayList), it fails with this error. The entity itself looks something like this:
#InheritConstructors
#EqualsAndHashCode
#ToString
#Table("user_data")
class UserData {
#PrimaryKey("id")
long userId
#Column
String name
}
It seems counter intuitive that I would have to create a special type of List just to save a collection of entities. Am I missing something? I'm using Spring Cassandra 1.3.4 with the DataStax driver core 2.1.9. Thanks.

Related

How might you manage IntegrationFlow Spring beans dynamically using a JPA database table?

I have a scenario in which I'm looking to register IntegrationFlow Spring beans based on the contents of a JPA database table.
For example, the table will look like:
#Entity
class IntegrationFlowConfig {
private long id;
private String local;
private String remote;
}
and I want an IntegrationFlow registered as a Spring bean for each entry found in the above table definition. When a row is added, a new bean is registered, and when a row is deleted, the corresponding bean is destroyed.
I've considered creating an EntityListener for the above entity, in which #PostPersist and #PostRemove will create/destroy IntegrationFlow beans via IntegrationFlowContext, but this solution seemed a bit clunky, and I was wondering if there was any functionality that exists that's a bit more streamlined to solve the above problem. Perhaps some sort of row mapping functionality that can map spring beans to JPA database rows, etc?
Any help would be much appreciated!
thanks,
Monk
Well, such an idea has crossed my mind several times in the past. But even with an XML configuration easily serialized into databased and deserialized into an Integration Flow (XML one) in its own child ApplicationContext, we still ended up with the problem that some beans have to be provided with their Java code. Even if we opted out for Groovy scripts, which could be parsed and loaded at runtime, some Java code would need to be compiled anyway. And in the end when we released some solution for the customer, it became very messy error prone how their operators wrote those dynamic flows.
You definitely can have some external configuration options and can have a conditional logic, but still the code must be compiled in advance without any way to let the logic (not data) to be seriailzed and deserialized at runtime.
Probably this is not an answer you are looking for, but there is no such a serialization solution and possibly it won't be done at all, since it is an anti-pattern (IMHO) to have dynamic application these days when we simply can deal with short and simple microservices or even functions.

Spring Data JDBC: Can I create my UUID PKs on the client side, and not on the server? [duplicate]

I'm playing around with spring-data-jdbc and discovered a problem, with I can't solve using Google.
No matter what I try to do, I just can't push a trivial object into the database (Bean1.java:25):
carRepository.save(new Car(2L, "BMW", "5"));
Both, without one and with a TransactionManager +#Transactional the database (apparently) does not commit the record.
The code is based on a Postgres database, but you might also simply use a H2 below and get the same result.
Here is the (minimalistic) source code:
https://github.com/bitmagier/spring-data-jdbc-sandbox/tree/stackoverflow-question
Can somebody tell me, why the car is not inserted into the database?
This is not related to transactions not working.
Instead, it's about Spring Data JDBC considering your instance an existing instance that needs updating (instead of inserting).
You can verify this is the problem by activating logging for org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate. You should see an update but no insert.
By default, Spring Data JDBC considers an entity as new when it has an id of an object type and a value of null or of a primitive type (e.g. int or long) and a value of 0.
If your entity has an attribute with #Version annotation that attribute will be used to determine if the instance is a new one.
You have the following options in order to make it work:
Set the id to null and configure your database schema so that it will automatically create a new value on insert. After the save your entity instance will contain the generated value from the database.
Note: Spring Data JDBC will set the id even if it is final in your entity.
Leave the id null and set it in a Before-Convert listener to the desired value.
Let your entity implement Persistable. This allows you to control when an entity is considered new. You'll probably need a listener as well so you can let the entity know it is not new any longer.
Beginning with version 1.1 of Spring Data JDBC you'll also be able to use a JdbcAggregateTemplate to do a direct insert, without inspecting the id, see https://jira.spring.io/browse/DATAJDBC-282. Of course, you can do that in a custom method of your repository, as is done in this example: https://github.com/spring-projects/spring-data-examples/pull/441

Trying to use Crate.io NoSql database with an existing Spring Data / Mysql project

I'm attempting to add Crate.IO capability to an existing Spring Data/Eclipselink/MySql web application. For this specific use case, we want to persist data to both MySql AND Crate (for evaluation purposes) in the most painless way possible. I'm using the Spring-Data-Crate project in order to be able to use Spring Data Repositories with Crate.
I've been able to setup a separate Crate specific entity manager with a filter to only utilize repos that implement CrateRepository. The problem I'm having is determining how to use the existing Spring Data/MySql entity classes with Crate. (or derive from them)
1) If I annotate an existing Spring Data #Entity class with the Spring-Data-Crate
#Table annotation, the mapping to the crate DB will fail because EclipseLink/JPA adds hidden persistence fields to entities objects that start with an underscore, which is apparently not allowed by the spring-data-crate adapter
2) I tried to use entity inheritance, with a base class that both the MySql and Crate entity can extend, with only the MySql entity having the spring data #Entity annotation. Unfortunately, this causes Spring Data to lose visibility of the base class fields unless the base class is annotated with #MappedSuperClass. But adding this annotation introduces the hidden "_"-prefixed persistence properties to the derived crate entity.
3) I could use separate entities entirely and have them implement a common interface, but I can't assign the interface as the type of the spring data crate repository.
... Not sure where to go from here
Spring Data Crate adapter project - https://github.com/KPTechnologyLab/spring-data-crate
Spring Data Crate Tutorial - https://crate.io/a/using-sprint-data-crate-with-your-java-rest-application/
i'm johannes from crate.
we didn't test the use of spring data crate in that manner so we can't state any information if this should or shouldn't work.
sorry, johannes

Why are my entities only partially populated when loading them using Spring Data JPA?

I'm using Spring Data JPA and DataNucleus as JPA persistence provider and have something like
interface BookRepository extends CrudRepository<Book, Long> {
Book findByAuthorId(Long id);
}
If I call bookRepository.findByAuthorId() and then access book.publishingHouse.manager.name is null. As opposed to calling bookRepository.findAll() when the fields are populated correctly all the way. I set datanucleus.DetachAllOnCommit=true and datanucleus.maxFetchDepth=-1 (I also tried with 10).
Any idea why?
If you don't have any additional transaction boundaries defined, the EntityManager closed when leaving the query method. That means what you get back is detached entities and what kind of load state you get back is determined by the defaults the persistence provider uses.
You basically have two options:
Have a client (service or controller class) using #Transactional to keep the EntityManager open and thus the loaded instances eligible to lazy-loading to pull data out of the store while you use the instance. If a controller or service is not enough, you might wanna look into the OpenEntityManagerInViewFilter/-Interceptor which basically keeps the EntityManager open until the view is rendered.
Define what should be fetched explicitly either using JPA 2.1 entity graphs (see the reference docs for details) or explicitly adding fetch-joins to the query by defining it manually.

Spring Data JPA Repository CRUD Testing

I'm playing around with the Spring Data Repository and have a question on writing CRUD tests. I have written many CRUD tests against Hibernate DAOs and EJB 3 entity beans where I create and entity, flush it to the database, clear the entity manager, and read it back by ID. The entity manager is cleared so the first level cache is not hit on the read.
Using the Spring Data repository I can't find a way to clear the underlying entity manager used by my test so my read is not going back to the actual database, making my test invalid.
Is there any way to clear the entity manager in the test? Is there a way I can inject one into my test so that it is used by the repository?
Thanks!
Cory.
Try it by injecting the entitymanager like this:
#PersistenceContext
EntityManager entityManager
and make your test transactional by setting the #Transactional attribute on your test method. Then inside the method you can call the entityManager.flush() method.
Regards
If you wish the EntityManager to be cleared automatically you can set #Modifying annotation’s clearAutomatically attribute to true.
Please see here