Sort Not Working - Accessing Data with JPA - spring

I've been playing with the Spring "Accessing Data with JPA" starter, adding my own entity and the corresponding repository.
#RepositoryRestResource(collectionResourceRel = "orders", path = "orders")
#CrossOrigin(maxAge = 3600)
public interface OrderRepository extends PagingAndSortingRepository<Order, Long> {
The controllerless routes work fine for simple paging, but there is nothing I can do to get sorting working.
There are two cases. One:
http://192.168.0.163:8080/orders?sort=order_id&order_id.dir=desc
In this case the sort argument has no effect. No matter what combination I try, the sort order is unaffected. I have turned on show queries in STS, and no "order by" clause is generated.
The second case is for some field names in sort=, there is a null pointer exception in Spring:
2016-10-07 14:32:48.426 DEBUG 8292 --- [nio-8080-exec-8] .w.s.m.m.a.ServletInvocableHandlerMethod : Error resolving argument [1] [type=org.springframework.data.rest.webmvc.support.DefaultedPageable]
HandlerMethod details:
Controller [org.springframework.data.rest.webmvc.RepositoryEntityController]
Method [public org.springframework.hateoas.Resources<?> org.springframework.data.rest.webmvc.RepositoryEntityController.getCollectionResource(org.springframework.data.rest.webmvc.RootResourceInformation,org.springframework.data.rest.webmvc.support.DefaultedPageable,org.springframework.data.domain.Sort,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler) throws org.springframework.data.rest.webmvc.ResourceNotFoundException,org.springframework.web.HttpRequestMethodNotSupportedException]
java.lang.NullPointerException: null
at org.springframework.data.rest.webmvc.json.JacksonMappingAwareSortTranslator$SortTranslator.translateSort(JacksonMappingAwareSortTranslator.java:101) ~[spring-data-rest-webmvc-2.5.3.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.json.JacksonMappingAwareSortTranslator.translateSort(JacksonMappingAwareSortTranslator.java:70) ~[spring-data-rest-webmvc-2.5.3.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.json.MappingAwareDefaultedPageableArgumentResolver.resolveArgument(MappingAwareDefaultedPageableArgumentResolver.java:73) ~[spring-data-rest-webmvc-2.5.3.RELEASE.jar:na]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) [spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) [spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114) [spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) [spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
...
I just downloaded STS and the starter this week so they should be the latest versions.

Too late, however.... avoid using underscores (e.g. 'aaa_bbb') inside property/field/getters-setters names of your Entities....

Related

Spring Boot #RequestScope and Hibernate schema based multi-tenancy

I'm working on a schema based multi-tenant app, in which I want to resolve the Tenant Identifier using a #RequestScope bean. My understanding is that #RequestScope uses/injects proxies for the request scoped beans, wherever they are referred (e.g. in other singleton beans). However, this is not working in the #Component that implements CurrentTenantIdentifierResolver and I get the following error when I start my service,
Caused by: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.userContext': Scope 'request' is not active for the current thread;
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Following are the relevant pieces of code.
#Component
public class CurrentTenant implements CurrentTenantIdentifierResolver {
#Autowired
private UserContext userContext;
#Override
public String resolveCurrentTenantIdentifier() {
return Optional.of(userContext)
.map(u -> u.getDomain())
.get();
}
#Component
#RequestScope
public class UserContext {
private UUID id;
private String domain;
My questions,
Isn't the proxy for the #RequestScope injected (by default)? Do I need to do anything more?
Is Hibernate/Spring trying to establish a connection to the DB at startup (even when there is no tenant available)?
Hibernate properties:
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
properties.remove(AvailableSettings.DEFAULT_SCHEMA);
properties.put(AvailableSettings.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
properties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
For the time being, I'm preventing the NullPointerException by checking if we are in the RequestContext. However, a connection still gets established to the master database (although I've explicitly specified the dialect and am not specifying hbm2ddl.auto). Since this connection is not associated with any schema, I'd like to avoid making it, so that it does not look for any tables that it won't find anyways.
What seems to be happenning is that when a HTTP request is received, hibernate is trying to resolve the current tenant identifier, even before my #RequestScope bean is created (and even before my #RestController method is called.) If a provide the default connection to the databse, I then get the following error. If I don't provide a connection, it throws an exception and aborts.
2021-09-26 11:55:44.882 WARN 19759 --- [nio-8082-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 42P01
2021-09-26 11:55:44.882 ERROR 19759 --- [nio-8082-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "employees" does not exist
Position: 301
2021-09-26 11:55:44.884 ERROR 19759 --- [nio-8082-exec-2] o.t.n.controller.EmployeeController : Exception: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet

Trying to reuse Spring JdbcTemplate connection for Postgres JDBC copyIn - getting 'relation does not exist'

I've hit a brick wall in a Spring/Kotlin/JDBC/Postgres project and am hoping the community can help.
What I'm Trying To Do
Use Spring JDBC API (JdbcTemplate) to create a temporary table (temporary_pokemon) in a Postgres database.
Unwrap the JDBC connection from the JdbcTemplate so I can use the same connection to load a CSV into the database using the Postgres JDBC driver's copyIn method (https://jdbc.postgresql.org/documentation/publicapi/org/postgresql/copy/CopyIn.html)
What Is Going Wrong
The copyIn method errors with the message relation "temporary_pokemon" does not exist, and my assumption here is that the unwrapped connection is somehow separate/different to the db.execute command which creates the table.
Ideally there's a way to re-use the same connection while still being able to rely largely on Spring Boot's autoconfiguration and things like automatic connection pooling, etc.
What I've Tried So Far
Adding the #Transactional annotation
Creating a DataSource manually using Spring Boot's DataSourceBuilder (this seems to work, I am assuming that it only creates a single connection which gets reused)
The error message
Caused by: org.postgresql.util.PSQLException: ERROR: relation "temporary_pokemon" does not exist
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553) ~[postgresql-42.2.18.jar:42.2.18]
at org.postgresql.core.v3.QueryExecutorImpl.processCopyResults(QueryExecutorImpl.java:1212) ~[postgresql-42.2.18.jar:42.2.18]
at org.postgresql.core.v3.QueryExecutorImpl.startCopy(QueryExecutorImpl.java:894) ~[postgresql-42.2.18.jar:42.2.18]
at org.postgresql.copy.CopyManager.copyIn(CopyManager.java:45) ~[postgresql-42.2.18.jar:42.2.18]
at org.postgresql.copy.CopyManager.copyIn(CopyManager.java:177) ~[postgresql-42.2.18.jar:42.2.18]
at org.postgresql.copy.CopyManager.copyIn(CopyManager.java:160) ~[postgresql-42.2.18.jar:42.2.18]
at com.example.demo.IngestPostgres.ingest(IngestPostgres.kt:32) ~[main/:na]
at com.example.demo.IngestPostgres$$FastClassBySpringCGLIB$$f1321c17.invoke(<generated>) ~[main/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.2.jar:5.3.2]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771) ~[spring-aop-5.3.2.jar:5.3.2]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.2.jar:5.3.2]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.3.2.jar:5.3.2]
... 14 common frames omitted
Code Snippets
My #Component for the CommandLineRunner:
#Component
class Seed : CommandLineRunner {
#Autowired
lateinit var ingester : IngestPostgres
override fun run(vararg args: String?) {
val buffer = BufferedReader(FileReader(File("src/main/resources/ingest.csv")))
ingester.ingest(buffer)
}
}
The IngestPostgres #Component:
#Component
class IngestPostgres {
#Autowired
private lateinit var db: JdbcTemplate
#Transactional
fun ingest(bufferedReader: BufferedReader) {
db.execute("""
DROP TABLE IF EXISTS temporary_pokemon;
CREATE TABLE temporary_pokemon (
pokemon_id INT,
pokemon_name VARCHAR,
pokemon_type VARCHAR
);
""".trimIndent())
val pgConnection = db.dataSource?.connection?.unwrap(PgConnection::class.java)!!
CopyManager(pgConnection).copyIn(
"COPY temporary_pokemon FROM stdin DELIMITER ',' CSV HEADER",
bufferedReader
)
// snipped - later code INSERTS contents of temporary_pokemon into main pokemon table
}
}
My dependencies in build.gradle.kts:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
Thanks for any help. I'm not an expert at Spring/JDBC by any means, so apologies in advance if I've missed something that's common knowledge. I've tried searching on SO and Google but to no avail.
Seems like your queries use different connections. So results of first query are not committed when the second query starts. You can get a single connection and use it for execution of both queries or use JdbcTemplate api for importing csv data to your table

What causes "HTTP method names must be tokens" error?

I simply cannot access url for my spring web applications anymore cause it always throws this error:
2020-10-05 15:18:02.599 INFO 13060 --- [nio-8083-exec-1] o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header
Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in method name [0x160x030x010x020x000x010x000x010xfc0x030x030x06m0xb9$0xccs0xc9D\0xecJA0x950x810xafM(0x1b0xbf0xad0x0d}y-}0x97S0xe70xe8e0xe30xee]. HTTP method names must be tokens
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:418) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:260) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.37.jar:9.0.37]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_261]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_261]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.37.jar:9.0.37]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_261]
I've tried to clean the browser history and cookies (using different browsers), I've tried to access it with http instead of https, I've tried to run several different projects (which used to work in the past). I don't know what else I could try to solve this error.
What could be causing it?
Controller:
#Controller
public class ProductController {
#Autowired
private ProductService productService;
#Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
#GetMapping("/products")
public ModelAndView products() {
ModelAndView mv = new ModelAndView();
List<Product> productList = (List<Product>) productService.getAllProducts();
mv.addObject("activeTabProducts", true);
mv.addObject("productList", productList);
return mv;
}
}
I don't know why, but it worked when I tried to access it from an anonymous tab
The interface you invoked has auth. You should add token to your cookie.
Following on from the suggestion by ALEXANDRE CHAGAS VIEIRA JUNIOR above, and how it also worked for me using Incognito/Private mode, proving it wasn't my application which was broken,...
So, it works in incognito. Fails with "invalid character found in method name http method names must be tokens" when normal window.
Tried clearing cookies. No luck.
Cause:
Turns out something had ended up forcing my http://localhost/thing to automatically redirect to https://localhost.thing, and hence this error.
Chrome Fix:
chrome://net-internals/#hsts
scroll to bottom, and for Domain: localhost, clicked the Delete button and this cleared out the stuff forcing the redirect to use https.
Edge was also affected which also then worked after this fix due to it being based on Chromium.
See also:
https://superuser.com/questions/565409/how-to-stop-an-automatic-redirect-from-http-to-https-in-chrome

Spring Integration beans aren't created when spring.main.lazy-initialization=true

With reference to my earlier question here
Spring Boot app fails to start when all beans are marked as Lazy, as it can't find an error channel
and a reference to the issue here:
https://github.com/spring-projects/spring-boot/issues/16184#issuecomment-480196051
does anyone know what beans need to be added to an instance of LazyInitializationExcludeFilter in order for Spring Integration to start when spring.main.lazy-initialization=true ?
I'm getting errors like below, saying that "myErrorChannel" bean isn't available, where this is defined in code like so:
#MessagingGateway(errorChannel = "myErrorChannel")
#FunctionalInterface
public interface SomeInterface{
}
How can I make the creation of the error channel eager rather than lazy ? Adding a LazyInitializationExcludeFilter and trying to filter out beans called "myErrorChannel" doesn't work, as there must be another (lazy) bean that isn't creating the errorChannel bean.
Stacktrace:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myErrorChannel' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:805)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1278)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
at org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:89)
at org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:46)
at org.springframework.integration.gateway.MessagingGatewaySupport.getErrorChannel(MessagingGatewaySupport.java:414)
at org.springframework.integration.graph.IntegrationGraphServer$NodeFactory.gatewayNode(IntegrationGraphServer.java:374)
at org.springframework.integration.graph.IntegrationGraphServer.lambda$gateways$5(IntegrationGraphServer.java:258)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet.lambda$entryConsumer$0(Collections.java:1577)
at java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1699)
at java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntrySetSpliterator.forEachRemaining(Collections.java:1602)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:485)
at org.springframework.integration.graph.IntegrationGraphServer.gateways(IntegrationGraphServer.java:263)
at org.springframework.integration.graph.IntegrationGraphServer.buildGraph(IntegrationGraphServer.java:184)
at org.springframework.integration.graph.IntegrationGraphServer.onApplicationEvent(IntegrationGraphServer.java:115)
at org.springframework.integration.graph.IntegrationGraphServer.onApplicationEvent(IntegrationGraphServer.java:66)
Solved by making any beans that are created in this manner as lazy:
#Bean
public IntegrationFlow someBeanName() {
return IntegrationFlows.from("someString")
.handle(restCallFailedHandler())
.handle(finishedHandler())
.get();
}

Spring data Cassandra Rest Id must be assignable to Serializable!: null

Given below Entity and Repository, I get Id must be assignable to Serializable!: null error when I access rest resource for repository.
curl -H 'Accept: application/json' http://localhost:8080/properties
{"cause":null,"message":"Id must be assignable to Serializable!: null"}
Groovy code
#Component
interface PropertyRepository extends CassandraRepository<Property, String> {
}
#Table("property_v1")
#Canonical
class Property {
#PrimaryKeyColumn(value = "name", type = PARTITIONED)
String name
#PrimaryKeyColumn(value = "environment", type = CLUSTERED)
String environment
#Column("value")
String value
}
I tried adding #Id annotation to primary key field but spring does not allow #Id and #PrimaryKeyColumn annotations on the same entity.
I get #Table types must not define both #Id and #PrimaryKeyColumn properties error.
How do I access spring data Cassandra entities over rest?
I tried using RepositoryRestResource annotation as well on Repository class but received same error.
#RepositoryRestResource(path = "/properties", collectionResourceRel = "properties")
Versions:
Spring boot: 2.0.1.RELEASE
Uses spring-boot-starter-data-cassandra, spring-boot-starter-data-rest moduldes
Exception Stacktrace:
java.lang.IllegalArgumentException: Id must be assignable to Serializable!: null
at org.springframework.util.Assert.instanceCheckFailed(Assert.java:637)
at org.springframework.util.Assert.isInstanceOf(Assert.java:537)
at org.springframework.data.rest.webmvc.support.RepositoryEntityLinks.linkToSingleResource(RepositoryEntityLinks.java:135)
at org.springframework.data.rest.core.support.DefaultSelfLinkProvider.createSelfLinkFor(DefaultSelfLinkProvider.java:68)
at org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.getSelfLinkFor(PersistentEntityResourceAssembler.java:99)
at org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.wrap(PersistentEntityResourceAssembler.java:76)
at org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.toResource(PersistentEntityResourceAssembler.java:55)
at org.springframework.data.rest.webmvc.AbstractRepositoryRestController.entitiesToResources(AbstractRepositoryRestController.java:110)
at org.springframework.data.rest.webmvc.AbstractRepositoryRestController.toResources(AbstractRepositoryRestController.java:80)
at org.springframework.data.rest.webmvc.RepositoryEntityController.getCollectionResource(RepositoryEntityController.java:209)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
Figured out the issue.
If an entity class has a composite key, spring data rest works only if I have a dedicated class for Primary Key columns.
Changing the class structure to below, enabled rest resources for spring data entities. I used a nested static class for key. But it could be very well a public class of its own.
I feel this boiler plate should be removed from developers and instead spring could look into partition key column and use it as Id.
#Component
interface PropertyRepository extends CassandraRepository<Property, Property.PropertyKey> {
}
#Table("property_v1")
#Canonical
class Property {
#PrimaryKey
PropertyKey key
#Column("value")
String value
#PrimaryKeyClass
#Canonical
static class PropertyKey implements Serializable {
#PrimaryKeyColumn(value = "name", type = PARTITIONED)
String name
#PrimaryKeyColumn(value = "environment", type = CLUSTERED)
String environment
}
}

Resources