'1'
This commit is contained in:
commit
b8618281d7
|
|
@ -0,0 +1,13 @@
|
|||
.project
|
||||
.classpath
|
||||
.springBeans
|
||||
.settings/
|
||||
target/
|
||||
|
||||
#IntelliJ Stuff
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
#Geode Stuff
|
||||
*.log
|
||||
*.dat
|
||||
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
|||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
language: java
|
||||
|
||||
os: linux
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- env: JDK='OpenJDK 16'
|
||||
before_install: wget https://github.com/sormuras/bach/raw/master/install-jdk.sh && . ./install-jdk.sh -f 16
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb http://packages.couchbase.com/releases/couchbase-server/community/deb/ xenial xenial/main'
|
||||
key_url: 'http://packages.couchbase.com/ubuntu/couchbase.key'
|
||||
packages:
|
||||
- couchbase-server-community
|
||||
|
||||
services:
|
||||
- redis
|
||||
- docker
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
- download
|
||||
|
||||
# See https://issues.couchbase.com/browse/MB-26556
|
||||
install:
|
||||
- curl -X POST http://127.0.0.1:8091/pools/default -d memoryQuota=2000 -d indexMemoryQuota=256 -d ftsMemoryQuota=256
|
||||
- curl -X POST http://127.0.0.1:8091/node/controller/setupServices -d "services=kv,index,n1ql,fts"
|
||||
- curl -X POST http://127.0.0.1:8091/settings/indexes -d "storageMode=forestdb"
|
||||
- curl -X POST http://127.0.0.1:8091/settings/web -d "username=Administrator&password=password&port=8091&"
|
||||
- /opt/couchbase/bin/cbdocloader -c 127.0.0.1:8091 -u Administrator -p password -b travel-sample -m 256 -d /opt/couchbase/samples/travel-sample.zip
|
||||
- /opt/couchbase/bin/couchbase-cli user-manage -c 127.0.0.1:8091 -u Administrator -p password --set --rbac-username=travel-sample --rbac-password=password --roles=admin --auth-domain local
|
||||
|
||||
script:
|
||||
- cp -f settings.xml $HOME/.m2/settings.xml
|
||||
- mvn -version
|
||||
- java -version
|
||||
- mvn clean test -U -Dsort -Dmaven.test.redirectTestOutputToFile=true -B -s settings.xml
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
= Spring Data Examples
|
||||
|
||||
image:https://travis-ci.org/spring-projects/spring-data-examples.svg?branch=main[Build Status,link=https://travis-ci.org/spring-projects/spring-data-examples]
|
||||
|
||||
This repository contains example projects for the different Spring Data modules to showcase the API and how to use the features provided by the modules.
|
||||
|
||||
We have separate folders for the samples of individual modules:
|
||||
|
||||
== Spring Data for Apache Cassandra
|
||||
|
||||
* `example` - Shows core Spring Data support for Apache Cassandra.
|
||||
* `kotlin` - Example for using Cassandra with Kotlin.
|
||||
* `reactive` - Example project to show reactive template and repository support.
|
||||
|
||||
== Spring Data Elasticsearch
|
||||
|
||||
* `example` - Example how to use basic text search, geo-spatial search and facets. It uses
|
||||
the High Level REST Client backing template and repository.
|
||||
* `reactive` - Example how to use reactive client, template and repository features.
|
||||
|
||||
Local Elasticsearch instance must be running to run the tests.
|
||||
|
||||
== Spring Data for Apache Geode
|
||||
|
||||
* `events` - In this example the test will make use of event handlers and async event
|
||||
queue to handle events.
|
||||
* `expiration-eviction` - In these examples the server is configured to delete entries
|
||||
after a certain idle period or after a Time-To-Live period (expiration0 or remove data
|
||||
from memory when certain thresholds are reached (eviction).
|
||||
* `function-invocation` - In this example the server will have 3 functions registered. The
|
||||
client will invoke each of the functions.
|
||||
* `queries` - In this example a client will query the data in various ways using OQl,
|
||||
continuous queries, and Apache Lucene indexes.
|
||||
* `security` - In this example the servers and clients are set up with security (
|
||||
username/password) authentication using Geode Security and Apache Shiro.
|
||||
* `storage` - In this example the server is configured to store data off of hte JVM heap
|
||||
using the `@EnableOffHeap` annotation and to compress region data using
|
||||
SnappyCompressor`.
|
||||
* `transactions` - In this example the client will perform operations within a
|
||||
transaction. First, it will do a successful transaction where entries are saved to the
|
||||
server, and then a failed transaction where all changes are reverted.
|
||||
* `wan` - In these example two servers are deployed. One server populates itself with data, and the other server gets populated with that data via WAN replication.
|
||||
|
||||
== Spring Data JDBC
|
||||
|
||||
* `basic` - Basic usage of Spring Data JDBC.
|
||||
* `immutables` - Showing Spring Data JDBC usage
|
||||
with https://immutables.github.io/[Immutables]
|
||||
|
||||
== Spring Data JPA
|
||||
|
||||
* `eclipselink` - Sample project to show how to use Spring Data JPA with Spring Boot and https://www.eclipse.org/eclipselink/[Eclipselink].
|
||||
* `example` - Probably the project you want to have a look at first. Contains a variety of sample packages, showcasing the different levels at which you can use Spring Data JPA. Have a look at the `simple` package for the most basic setup.
|
||||
* `interceptors` - Example of how to enrich the repositories with AOP.
|
||||
* `jpa21` - Shows support for JPA 2.1 specific features (stored procedures support).
|
||||
* `multiple-datasources` - Examples of how to use Spring Data JPA with multiple `DataSource`s.
|
||||
* `query-by-example` - Example project showing usage of Query by Example with Spring Data JPA.
|
||||
* `security` - Example of how to integrate Spring Data JPA Repositories with Spring Security.
|
||||
* `showcase` - Refactoring show case of how to improve a plain-JPA-based persistence layer by using Spring Data JPA (read: removing close to all of the implementation code). Follow the `demo.txt` file for detailed instructions.
|
||||
* `vavr` - Shows the support of https://www.vavr.io[Vavr] collection types as return types for query methods.
|
||||
|
||||
== Spring Data LDAP
|
||||
|
||||
* `example` - Sample for Spring Data repositories to access an LDAP store.
|
||||
|
||||
== Spring Data MongoDB
|
||||
|
||||
* `aggregation` - Example project to showcase the MongoDB aggregation framework support.
|
||||
* `example` - Example project for general repository functionality (including geo-spatial functionality), Querydsl integration and advanced topics.
|
||||
* `fluent-api` - Example project to show the new fluent API (`MongoTemplate`-alternative) to interact with MongoDB.
|
||||
* `geo-json` - Example project showing usage of http://geojson.org[GeoJSON] with MongoDB.
|
||||
* `gridfs` - Example project showing usage of gridFS with MongoDB.
|
||||
* `jmolecules` - Example of Spring Data MongoDB working with a jMolecules based domain model.
|
||||
* `kotlin` - Example for using https://kotlinlang.org/[Kotlin] with MongoDB.
|
||||
* `linking` - Example demonstrating possibilities for linking documents.
|
||||
* `query-by-example` - Example project showing usage of Query by Example with MongoDB.
|
||||
* `querydsl` - Example project showing imperative and reactive https://github.com/querydsl/querydsl[Querydsl] support for MongoDB.
|
||||
* `reactive` - Example project to show reactive template and repository support.
|
||||
* `repository-metrics` - Example project to show how to collect repository method invocation metrics.
|
||||
* `security` - Example project showing usage of Spring Security with MongoDB.
|
||||
* `text-search` - Example project showing usage of MongoDB text search feature.
|
||||
* `transactions` - Example project for imperative and reactive MongoDB 4.0 transaction support.
|
||||
|
||||
== Spring Data Neo4j
|
||||
|
||||
* `example` - Example to show basic node and relationship entities and repository usage.
|
||||
|
||||
== Spring Data R2DBC
|
||||
|
||||
* `example` - Basic usage of Spring Data R2DBC.
|
||||
|
||||
== Spring Data Redis
|
||||
|
||||
* `cluster` - Example for Redis Cluster support.
|
||||
* `example` - Example for basic Spring Data Redis setup.
|
||||
* `reactive` - Example project to show reactive template support.
|
||||
* `repositories` - Example demonstrating Spring Data repository abstraction on top of Redis.
|
||||
* `sentinel` - Example for Redis Sentinel support.
|
||||
* `streams` - Example for https://redis.io/topics/streams-intro[Redis Streams] support.
|
||||
|
||||
Local Redis instances must be running to run the tests. One option is to use Docker in a separate terminal:
|
||||
|
||||
```
|
||||
$ docker run -p 6379:6379 redis:5.0
|
||||
```
|
||||
|
||||
WARNING: If you're done using it, don't forget to shut it down!
|
||||
|
||||
== Spring Data REST
|
||||
|
||||
* `headers` - A sample showing the population of HTTP headers and the usage of them to perform conditional `GET` requests.
|
||||
* `multi-store` - A sample REST web-service based on both Spring Data JPA and Spring Data MongoDB.
|
||||
* `projections` - A sample REST web-service showing how to use projections.
|
||||
* `security` - A sample REST web-service secured using Spring Security.
|
||||
* `starbucks` - A sample REST web-service built with Spring Data REST and MongoDB.
|
||||
* `uri-customizations` - Example project to show URI customization capabilities.
|
||||
|
||||
== Spring Data web support
|
||||
|
||||
* `projections` - Example for Spring Data web support for JSONPath and XPath expressions on projection interfaces.
|
||||
* `querydsl` - Example for Spring Data Querydsl web integration (creating a `Predicate` from web requests).
|
||||
* `web` - Example for Spring Data web integration (binding `Pageable` instances to Spring MVC controller methods, using interfaces to bind Spring MVC request payloads).
|
||||
|
||||
== Miscellaneous
|
||||
|
||||
* `bom` - Example project how to use the Spring Data release train bom in non-Spring-Boot
|
||||
scenarios.
|
||||
* `map` - Example project to show how to use `Map`-backed repositories.
|
||||
* `multi-store` - Example project to use both Spring Data MongoDB and Spring Data JPA in
|
||||
one project.
|
||||
|
||||
== Note
|
||||
|
||||
* The example projects make use of the https://projectlombok.org/[Lombok] plugin. To get
|
||||
proper code navigation in your IDE, you must install it separately. Lombok is available
|
||||
in the IntelliJ plugins repository and as
|
||||
a https://projectlombok.org/download[download] for Eclipse-based IDEs.
|
||||
* The code makes use of Java 16 language features therefore you need Java 16 or newer to
|
||||
run and compile the examples.
|
||||
* Most store modules examples start their database via Testcontainers or as
|
||||
embedded/in-memory server unless stated otherwise.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.gradle
|
||||
/build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
= Spring Data - Release Train BOM example
|
||||
|
||||
This project shows the usage of the Spring Data BOM in a non-Spring-Boot project with both Maven and Gradle.
|
||||
|
||||
== Properties
|
||||
|
||||
In both Maven and Gradle a couple of properties are used to define the versions of Spring Framework and Spring Data to use. For Spring Framework a plain version is used. For Spring Data we refer to the https://spring.io/blog/2020/04/30/updates-to-spring-versions[calver revision] of the BOM. The naming of Spring Data releases uses the following conventions:
|
||||
|
||||
** `${calver-version}-M1` -> Milestones
|
||||
** …
|
||||
** `${calver-version}-RC1` -> Release candidate
|
||||
** …
|
||||
** `${calver-version}` -> GA version
|
||||
** `${calver-version}` -> Services release (bugfixes) for that release train
|
||||
|
||||
== Maven
|
||||
|
||||
The `<dependencyManagement />` section declares dependencies to the BOMs for both Spring and Spring Data, using the `import` scope and `pom` type.
|
||||
|
||||
The standard `<dependencies />` section can now list Spring Framework and Spring Data dependencies without declaring a version and still be sure all libraries are in matching versions.
|
||||
|
||||
Note, that we don't declare a Spring Framework dependency here. The import of the Spring Framework BOM nonetheless makes sure we control the version of all transitive Spring Framework dependencies pulled in by the Spring Data modules.
|
||||
|
||||
== Gradle
|
||||
|
||||
Gradle does not support Maven BOMs (Bill of Materials) out of the box, so the first thing to do is important the
|
||||
https://github.com/spring-gradle-plugins/dependency-management-plugin[dependency management plugin]. This example is based on Java,
|
||||
but if you need a different language plugin (e.g. Kotlin), you can do so.
|
||||
|
||||
The `dependencyManagement` section can be used to import the Spring Framework BOM and Spring Data BOM.
|
||||
|
||||
The standard `dependencies` section can now list Spring and Spring Data dependencies without declaring a version and still
|
||||
be sure all libraries are align with each other.
|
||||
|
||||
Note how you don't declare a Spring Framework dependency. Nevertheless, the dependency management plugin and the Spring Framework BOM
|
||||
ensures you control the version of all transitive Spring Framework dependencies pulled in by Spring Data.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.6.RELEASE'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
ext {
|
||||
springVersion = '5.3.9'
|
||||
springDataVersion = '2021.0.4'
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "org.springframework:spring-framework-bom:${springVersion}"
|
||||
mavenBom "org.springframework.data:spring-data-bom:${springDataVersion}"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.springframework.data:spring-data-rest-webmvc'
|
||||
compile 'org.springframework.data:spring-data-jpa'
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-examples-bom</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
|
||||
<name>Spring Data - Using the BOM for dependency management</name>
|
||||
|
||||
<properties>
|
||||
<spring.version>5.3.9</spring.version>
|
||||
<spring-data.version>2021.0.4</spring-data.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-bom</artifactId>
|
||||
<version>${spring-data.version}</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-rest-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-cassandra-example</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-cassandra-examples</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Cassandra - Example</name>
|
||||
<description>Basic sample project showing the usage of Spring Data Cassandra.</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-cassandra-example-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
/**
|
||||
* Simple domain object that declares properties annotated with Spring Data's annotations for auditing.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Table
|
||||
public class AuditedPerson implements Persistable<Long> {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
@CreatedBy String createdBy;
|
||||
|
||||
@LastModifiedBy String lastModifiedBy;
|
||||
|
||||
@CreatedDate Instant createdDate;
|
||||
|
||||
@LastModifiedDate Instant lastModifiedDate;
|
||||
|
||||
@Transient boolean isNew;
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return isNew;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* Simple repository interface for {@link AuditedPerson} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface AuditedPersonRepository extends CrudRepository<AuditedPerson, Long> {}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.cassandra.config.EnableCassandraAuditing;
|
||||
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
|
||||
/**
|
||||
* Basic {@link Configuration} to create the necessary schema for the {@link AuditedPerson} table.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan(basePackageClasses = AuditedPerson.class)
|
||||
@EnableCassandraAuditing
|
||||
class BasicConfiguration {
|
||||
|
||||
/**
|
||||
* {@code @Bean} method defining a supplier for an auditor. This could be also an integration with a security
|
||||
* framework such as Spring Security.
|
||||
*/
|
||||
@Bean
|
||||
AuditorAware<String> auditorAware() {
|
||||
return () -> Optional.of("Some user");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code @Bean} method defining a {@link MappingCassandraConverter} as currently the auditing requires a bean
|
||||
* definition for {@link MappingCassandraConverter}.
|
||||
*/
|
||||
@Bean
|
||||
public MappingCassandraConverter cassandraConverter(CassandraMappingContext mapping,
|
||||
CassandraCustomConversions conversions, CqlSession session) {
|
||||
|
||||
var converter = new MappingCassandraConverter(mapping);
|
||||
|
||||
converter.setCodecRegistry(session.getContext().getCodecRegistry());
|
||||
converter.setCustomConversions(conversions);
|
||||
converter.setUserTypeResolver(new SimpleUserTypeResolver(session));
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.basic;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Basic {@link Configuration} to create the necessary schema for the {@link User} table.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan(basePackageClasses = User.class)
|
||||
class BasicConfiguration {
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.basic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* Simple repository interface for {@link User} instances. The interface is used to declare so called query methods,
|
||||
* methods to retrieve single entities or collections of them.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public interface BasicUserRepository extends CrudRepository<User, Long> {
|
||||
|
||||
/**
|
||||
* Sample method annotated with {@link Query}. This method executes the CQL from the {@link Query} value.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@Query("SELECT * from users where user_id in(?0)")
|
||||
User findUserByIdIn(long id);
|
||||
|
||||
/**
|
||||
* Derived query method. This query corresponds with {@code SELECT * FROM users WHERE uname = ?0}.
|
||||
* {@link User#username} is not part of the primary so it requires a secondary index.
|
||||
*
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
User findUserByUsername(String username);
|
||||
|
||||
/**
|
||||
* Derived query method using SASI (SSTable Attached Secondary Index) features through the {@code LIKE} keyword. This
|
||||
* query corresponds with {@code SELECT * FROM users WHERE lname LIKE '?0'}. {@link User#lastname} is not part of the
|
||||
* primary key so it requires a secondary index.
|
||||
*
|
||||
* @param lastnamePrefix
|
||||
* @return
|
||||
*/
|
||||
List<User> findUsersByLastnameStartsWith(String lastnamePrefix);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.basic;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.springframework.data.cassandra.core.mapping.Column;
|
||||
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* Sample user class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Table(value = "users")
|
||||
public class User {
|
||||
|
||||
@PrimaryKey("user_id") private Long id;
|
||||
|
||||
@Column("uname") private String username;
|
||||
@Column("fname") private String firstname;
|
||||
@Column("lname") private String lastname;
|
||||
|
||||
public User(Long id) {
|
||||
this.setId(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* Package showing a simple repository interface to use basic query method execution functionality.
|
||||
*/
|
||||
package example.springdata.cassandra.basic;
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
||||
import org.springframework.data.cassandra.core.mapping.Element;
|
||||
import org.springframework.data.cassandra.core.mapping.Tuple;
|
||||
|
||||
/**
|
||||
* Simple mapped tuple.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Tuple
|
||||
public record Address(@Element(0) String address, @Element(1) String city, @Element(2) String zip) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* Sample Addressbook class.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Table
|
||||
public class Addressbook {
|
||||
|
||||
@Id String id;
|
||||
|
||||
Contact me;
|
||||
List<Contact> friends;
|
||||
|
||||
Address address;
|
||||
Map<Integer, Currency> preferredCurrencies;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
||||
/**
|
||||
* Sample Contact class.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public record Contact(String firstname, String lastname) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link Configuration} class to register custom converters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan(basePackageClasses = Addressbook.class)
|
||||
class ConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
public CassandraCustomConversions customConversions() {
|
||||
|
||||
List<Converter<?, ?>> converters = new ArrayList<>();
|
||||
converters.add(new PersonWriteConverter());
|
||||
converters.add(new PersonReadConverter());
|
||||
converters.add(new CustomAddressbookReadConverter());
|
||||
converters.add(CurrencyToStringConverter.INSTANCE);
|
||||
converters.add(StringToCurrencyConverter.INSTANCE);
|
||||
|
||||
return new CassandraCustomConversions(converters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a {@link Contact} into its {@link String} representation.
|
||||
*/
|
||||
static class PersonWriteConverter implements Converter<Contact, String> {
|
||||
|
||||
public String convert(Contact source) {
|
||||
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(source);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a {@link Contact} from its {@link String} representation.
|
||||
*/
|
||||
static class PersonReadConverter implements Converter<String, Contact> {
|
||||
|
||||
public Contact convert(String source) {
|
||||
|
||||
if (StringUtils.hasText(source)) {
|
||||
try {
|
||||
return new ObjectMapper().readValue(source, Contact.class);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform custom mapping by reading a {@link Row} into a custom class.
|
||||
*/
|
||||
static class CustomAddressbookReadConverter implements Converter<Row, CustomAddressbook> {
|
||||
|
||||
public CustomAddressbook convert(Row source) {
|
||||
|
||||
return new CustomAddressbook(source.getString("id"), source.getString("me"));
|
||||
}
|
||||
}
|
||||
|
||||
enum StringToCurrencyConverter implements Converter<String, Currency> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Currency convert(String source) {
|
||||
return Currency.getInstance(source);
|
||||
}
|
||||
}
|
||||
|
||||
enum CurrencyToStringConverter implements Converter<Currency, String> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(Currency source) {
|
||||
return source.getCurrencyCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public record CustomAddressbook(String theId, String myDetailsAsJson) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Package showing conversion features.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.events;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Basic {@link Configuration} to create the necessary schema for the {@link User} table.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan(basePackageClasses = User.class)
|
||||
class BasicConfiguration {
|
||||
|
||||
@Bean
|
||||
LoggingEventListener listener() {
|
||||
return new LoggingEventListener();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.events;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener;
|
||||
import org.springframework.data.cassandra.core.mapping.event.AfterConvertEvent;
|
||||
import org.springframework.data.cassandra.core.mapping.event.AfterDeleteEvent;
|
||||
import org.springframework.data.cassandra.core.mapping.event.AfterLoadEvent;
|
||||
import org.springframework.data.cassandra.core.mapping.event.AfterSaveEvent;
|
||||
import org.springframework.data.cassandra.core.mapping.event.BeforeDeleteEvent;
|
||||
import org.springframework.data.cassandra.core.mapping.event.BeforeSaveEvent;
|
||||
|
||||
/**
|
||||
* {@link ApplicationListener} for Cassandra mapping events logging the events.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class LoggingEventListener extends AbstractCassandraEventListener<Object> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingEventListener.class);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener#onBeforeSave(org.springframework.data.cassandra.core.mapping.event.BeforeSaveEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onBeforeSave(BeforeSaveEvent<Object> event) {
|
||||
LOGGER.info("onBeforeSave: {}, {}", event.getSource(), event.getStatement());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener#onAfterSave(org.springframework.data.cassandra.core.mapping.event.AfterSaveEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onAfterSave(AfterSaveEvent<Object> event) {
|
||||
LOGGER.info("onAfterSave: {}", event.getSource());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener#onBeforeDelete(org.springframework.data.cassandra.core.mapping.event.BeforeDeleteEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onBeforeDelete(BeforeDeleteEvent<Object> event) {
|
||||
LOGGER.info("onBeforeDelete: {}", event.getSource());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener#onAfterDelete(org.springframework.data.cassandra.core.mapping.event.AfterDeleteEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onAfterDelete(AfterDeleteEvent<Object> event) {
|
||||
LOGGER.info("onAfterDelete: {}", event.getSource());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener#onAfterLoad(org.springframework.data.cassandra.core.mapping.event.AfterLoadEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onAfterLoad(AfterLoadEvent<Object> event) {
|
||||
LOGGER.info("onAfterLoad: {}", event.getSource());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener#onAfterConvert(org.springframework.data.cassandra.core.mapping.event.AfterConvertEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onAfterConvert(AfterConvertEvent<Object> event) {
|
||||
LOGGER.info("onAfterConvert: {}", event.getSource());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.events;
|
||||
|
||||
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* Sample user class.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table(value = "users")
|
||||
public record User(@PrimaryKey long id, String firstname, String lastname) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Package usage of Lifecycle events.
|
||||
*/
|
||||
package example.springdata.cassandra.events;
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.optimisticlocking;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Basic {@link Configuration} to create the necessary schema for the {@link OptimisticPerson} table.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan(basePackageClasses = OptimisticPerson.class)
|
||||
class BasicConfiguration {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.optimisticlocking;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* Simple domain object that declares properties annotated with Spring Data's {@code @Version} annotation to enable the
|
||||
* object for optimistic locking.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table
|
||||
public record OptimisticPerson(@Id Long id, @Version long version, String name) {
|
||||
|
||||
public OptimisticPerson withName(String name) {
|
||||
return new OptimisticPerson(id, version, name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.optimisticlocking;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* Simple repository interface for {@link OptimisticPerson} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface OptimisticPersonRepository extends CrudRepository<OptimisticPerson, Long> {}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.optimisticlocking;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* Simple domain object.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Table
|
||||
public class SimplePerson {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
String name;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table
|
||||
record Customer(@Id String id, String firstname, String lastname) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
||||
/**
|
||||
* An example projection interface containing only the firstname.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface CustomerProjection {
|
||||
|
||||
String getFirstname();
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* Sample repository managing customers to show projecting functionality of Spring Data Cassandra.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface CustomerRepository extends CrudRepository<Customer, String> {
|
||||
|
||||
/**
|
||||
* Uses a projection interface to indicate the fields to be returned. As the projection doesn't use any dynamic
|
||||
* fields, the query execution will be restricted to only the fields needed by the projection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Collection<CustomerProjection> findAllProjectedBy();
|
||||
|
||||
/**
|
||||
* When a projection is used that contains dynamic properties (i.e. SpEL expressions in an {@link Value} annotation),
|
||||
* the normal target entity will be loaded but dynamically projected so that the target can be referred to in the
|
||||
* expression.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Collection<CustomerSummary> findAllSummarizedBy();
|
||||
|
||||
/**
|
||||
* Passes in the projection type dynamically.
|
||||
*
|
||||
* @param id
|
||||
* @param projection
|
||||
* @return
|
||||
*/
|
||||
<T> Collection<T> findById(String id, Class<T> projection);
|
||||
|
||||
/**
|
||||
* Projection for a single entity.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
CustomerProjection findProjectedById(String id);
|
||||
|
||||
/**
|
||||
* Dynamic projection for a single entity.
|
||||
*
|
||||
* @param id
|
||||
* @param projection
|
||||
* @return
|
||||
*/
|
||||
<T> T findProjectedById(String id, Class<T> projection);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* An example of using SpEL with projections.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface CustomerSummary {
|
||||
|
||||
@Value("#{target.firstname + ' ' + target.lastname}")
|
||||
String getFullName();
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Basic {@link Configuration} to create the necessary schema for the {@link Customer} table.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan(basePackageClasses = Customer.class)
|
||||
class ProjectionConfiguration {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Package showing projection features.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
class CassandraConfiguration {
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table("pizza_orders")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Order {
|
||||
|
||||
@Id String id;
|
||||
LocalDate orderDate;
|
||||
ZoneId zoneId;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
/**
|
||||
* Repository to manage {@link Order} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface OrderRepository extends Repository<Order, String> {
|
||||
|
||||
/**
|
||||
* Method parameters are converted according the registered Converters into Cassandra types.
|
||||
*/
|
||||
@Query("SELECT * from pizza_orders WHERE orderdate = ?0 and zoneid = ?1 ALLOW FILTERING")
|
||||
Order findOrderByOrderDateAndZoneId(LocalDate orderDate, ZoneId zoneId);
|
||||
|
||||
void deleteAll();
|
||||
|
||||
Order save(Order order);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Person {
|
||||
|
||||
@Id String id;
|
||||
String firstname, lastname;
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
/**
|
||||
* Repository to manage {@link Person} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface PersonRepository extends Repository<Person, String> {
|
||||
|
||||
Optional<Person> findById(String id);
|
||||
|
||||
Stream<Person> findAll();
|
||||
|
||||
/**
|
||||
* Sample method to derive a query from using JDK 8's {@link Optional} as return type.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@Query("select * from person where id = ?0")
|
||||
Optional<Person> findPersonById(String id);
|
||||
|
||||
/**
|
||||
* Sample default method to show JDK 8 feature support.
|
||||
*
|
||||
* @param person
|
||||
* @return
|
||||
*/
|
||||
default Optional<Person> findByPerson(Person person) {
|
||||
return findPersonById(person == null ? null : person.id);
|
||||
}
|
||||
|
||||
void deleteAll();
|
||||
|
||||
Person save(Person person);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Package showing Java 8 features.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.udt;
|
||||
|
||||
import org.springframework.data.cassandra.core.mapping.UserDefinedType;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@UserDefinedType
|
||||
public record Address(String street, String zip, String city) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.udt;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraType;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
import com.datastax.oss.driver.api.core.data.UdtValue;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Table
|
||||
public class Person {
|
||||
|
||||
@Id int id;
|
||||
|
||||
String firstname, lastname;
|
||||
Address current;
|
||||
List<Address> previous;
|
||||
|
||||
@CassandraType(type = CassandraType.Name.UDT, userTypeName = "address") UdtValue alternative;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
spring.data.cassandra.keyspace-name=example
|
||||
spring.data.cassandra.schema-action=recreate
|
||||
spring.data.cassandra.local-datacenter=datacenter1
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* Integration test showing the basic usage of Auditing through {@link AuditedPersonRepository}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest(classes = BasicConfiguration.class)
|
||||
@CassandraKeyspace
|
||||
class AuditedPersonRepositoryTests {
|
||||
|
||||
@Autowired AuditedPersonRepository repository;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving an object using the Cassandra Repository will create a persistent representation of the object in Cassandra.
|
||||
*/
|
||||
@Test
|
||||
void insertShouldSetCreatedDate() {
|
||||
|
||||
var person = new AuditedPerson();
|
||||
person.setId(42L);
|
||||
person.setNew(true); // Cassandra does not support auto-generation hence we need
|
||||
// to supply whether our object is a new one.
|
||||
var saved = repository.save(person);
|
||||
|
||||
assertThat(saved.getCreatedBy()).isEqualTo("Some user");
|
||||
assertThat(saved.getLastModifiedBy()).isEqualTo("Some user");
|
||||
assertThat(saved.getCreatedDate()).isBetween(Instant.now().minus(Duration.ofMinutes(1)),
|
||||
Instant.now().plus(Duration.ofMinutes(1)));
|
||||
assertThat(saved.getLastModifiedDate()).isBetween(Instant.now().minus(Duration.ofMinutes(1)),
|
||||
Instant.now().plus(Duration.ofMinutes(1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifying an existing object will update the last modified fields.
|
||||
*/
|
||||
@Test
|
||||
void updateShouldSetLastModifiedDate() {
|
||||
|
||||
var person = new AuditedPerson();
|
||||
person.setId(42L);
|
||||
person.setNew(true); // Cassandra does not support auto-generation hence we need
|
||||
// to supply whether our object is a new one.
|
||||
repository.save(person);
|
||||
|
||||
person.setNew(false);
|
||||
|
||||
var modified = repository.save(person);
|
||||
|
||||
assertThat(modified.getCreatedBy()).isEqualTo("Some user");
|
||||
assertThat(modified.getLastModifiedBy()).isEqualTo("Some user");
|
||||
assertThat(modified.getCreatedDate()).isBetween(Instant.now().minus(Duration.ofMinutes(1)),
|
||||
Instant.now().plus(Duration.ofMinutes(1)));
|
||||
assertThat(modified.getLastModifiedDate())
|
||||
.isBetween(Instant.now().minus(Duration.ofMinutes(1)), Instant.now().plus(Duration.ofMinutes(1)))
|
||||
.isNotEqualTo(modified.getCreatedDate());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.basic;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.Assumptions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
import example.springdata.cassandra.util.CassandraVersion;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.util.Version;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
|
||||
/**
|
||||
* Integration test showing the basic usage of {@link BasicUserRepository}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest(classes = BasicConfiguration.class)
|
||||
@CassandraKeyspace
|
||||
class BasicUserRepositoryTests {
|
||||
|
||||
private final static Version CASSANDRA_3_4 = Version.parse("3.4");
|
||||
|
||||
@Autowired BasicUserRepository repository;
|
||||
@Autowired CqlSession session;
|
||||
private User user;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
user = new User();
|
||||
user.setId(42L);
|
||||
user.setUsername("foobar");
|
||||
user.setFirstname("firstname");
|
||||
user.setLastname("lastname");
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving an object using the Cassandra Repository will create a persistent representation of the object in Cassandra.
|
||||
*/
|
||||
@Test
|
||||
void findSavedUserById() {
|
||||
|
||||
user = repository.save(user);
|
||||
|
||||
assertThat(repository.findById(user.getId())).contains(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cassandra can be queries by using query methods annotated with {@link @Query}.
|
||||
*/
|
||||
@Test
|
||||
void findByAnnotatedQueryMethod() {
|
||||
|
||||
repository.save(user);
|
||||
|
||||
assertThat(repository.findUserByIdIn(1000)).isNull();
|
||||
assertThat(repository.findUserByIdIn(42)).isEqualTo(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring Data Cassandra supports query derivation so annotating query methods with
|
||||
* {@link org.springframework.data.cassandra.repository.Query} is optional. Querying columns other than the primary
|
||||
* key requires a secondary index.
|
||||
*/
|
||||
@Test
|
||||
void findByDerivedQueryMethod() throws InterruptedException {
|
||||
|
||||
session.execute("CREATE INDEX IF NOT EXISTS user_username ON users (uname);");
|
||||
/*
|
||||
Cassandra secondary indexes are created in the background without the possibility to check
|
||||
whether they are available or not. So we are forced to just wait. *sigh*
|
||||
*/
|
||||
Thread.sleep(1000);
|
||||
|
||||
repository.save(user);
|
||||
|
||||
assertThat(repository.findUserByUsername(user.getUsername())).isEqualTo(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring Data Cassandra supports {@code LIKE} and {@code CONTAINS} query keywords to for SASI indexes.
|
||||
*/
|
||||
@Test
|
||||
void findByDerivedQueryMethodWithSASI() throws InterruptedException {
|
||||
|
||||
assumeThat(CassandraVersion.getReleaseVersion(session).isGreaterThanOrEqualTo(CASSANDRA_3_4)).isTrue();
|
||||
|
||||
session.execute("CREATE CUSTOM INDEX ON users (lname) USING 'org.apache.cassandra.index.sasi.SASIIndex';");
|
||||
/*
|
||||
Cassandra secondary indexes are created in the background without the possibility to check
|
||||
whether they are available or not. So we are forced to just wait. *sigh*
|
||||
*/
|
||||
Thread.sleep(1000);
|
||||
|
||||
repository.save(user);
|
||||
|
||||
assertThat(repository.findUsersByLastnameStartsWith("last")).contains(user);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.basic;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.cassandra.core.AsyncCassandraTemplate;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.CassandraTemplate;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
|
||||
import com.datastax.oss.driver.api.querybuilder.insert.Insert;
|
||||
|
||||
/**
|
||||
* Integration test showing the basic usage of {@link CassandraTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest(classes = BasicConfiguration.class)
|
||||
@CassandraKeyspace
|
||||
class CassandraOperationsIntegrationTests {
|
||||
|
||||
|
||||
@Autowired CqlSession session;
|
||||
@Autowired CassandraOperations template;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
template.getCqlOperations().execute("TRUNCATE users");
|
||||
}
|
||||
|
||||
/**
|
||||
* Cassandra {@link com.datastax.driver.core.Statement}s can be used together with {@link CassandraTemplate} and the
|
||||
* mapping layer.
|
||||
*/
|
||||
@Test
|
||||
void insertAndSelect() {
|
||||
|
||||
var insert = QueryBuilder.insertInto("users").value("user_id", QueryBuilder.literal(42L)) //
|
||||
.value("uname", QueryBuilder.literal("heisenberg")) //
|
||||
.value("fname", QueryBuilder.literal("Walter")) //
|
||||
.value("lname", QueryBuilder.literal("White")) //
|
||||
.ifNotExists(); //
|
||||
|
||||
template.getCqlOperations().execute(insert.asCql());
|
||||
|
||||
var user = template.selectOneById(42L, User.class);
|
||||
assertThat(user.getUsername()).isEqualTo("heisenberg");
|
||||
|
||||
var users = template.select(QueryBuilder.selectFrom("users").all().asCql(), User.class);
|
||||
assertThat(users).hasSize(1);
|
||||
assertThat(users.get(0)).isEqualTo(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Objects can be inserted and updated using {@link CassandraTemplate}. What you {@code update} is what you
|
||||
* {@code select}.
|
||||
*/
|
||||
@Test
|
||||
void insertAndUpdate() {
|
||||
|
||||
var user = new User();
|
||||
user.setId(42L);
|
||||
user.setUsername("heisenberg");
|
||||
user.setFirstname("Walter");
|
||||
user.setLastname("White");
|
||||
|
||||
template.insert(user);
|
||||
|
||||
user.setFirstname(null);
|
||||
template.update(user);
|
||||
|
||||
var loaded = template.selectOneById(42L, User.class);
|
||||
assertThat(loaded.getUsername()).isEqualTo("heisenberg");
|
||||
assertThat(loaded.getFirstname()).isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous query execution using callbacks.
|
||||
*/
|
||||
@Test
|
||||
void insertAsynchronously() throws InterruptedException {
|
||||
|
||||
var user = new User();
|
||||
user.setId(42L);
|
||||
user.setUsername("heisenberg");
|
||||
user.setFirstname("Walter");
|
||||
user.setLastname("White");
|
||||
|
||||
final var countDownLatch = new CountDownLatch(1);
|
||||
|
||||
var asyncTemplate = new AsyncCassandraTemplate(session);
|
||||
|
||||
var future = asyncTemplate.insert(user);
|
||||
|
||||
future.addCallback(it -> countDownLatch.countDown(), throwable -> countDownLatch.countDown());
|
||||
|
||||
countDownLatch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
var loaded = template.selectOneById(user.getId(), User.class);
|
||||
assertThat(loaded).isEqualTo(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CassandraTemplate} allows selection of projections on template-level. All basic data types including
|
||||
* {@link Row} can be selected.
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void selectProjections() {
|
||||
|
||||
var user = new User();
|
||||
user.setId(42L);
|
||||
user.setUsername("heisenberg");
|
||||
user.setFirstname("Walter");
|
||||
user.setLastname("White");
|
||||
|
||||
template.insert(user);
|
||||
|
||||
var id = template.selectOne(QueryBuilder.selectFrom("users").column("user_id").asCql(), Long.class);
|
||||
assertThat(id).isEqualTo(user.getId());
|
||||
|
||||
var row = template.selectOne(QueryBuilder.selectFrom("users").column("user_id").asCql(), Row.class);
|
||||
assertThat(row.getLong(0)).isEqualTo(user.getId());
|
||||
|
||||
Map<String, Object> map = template.selectOne(QueryBuilder.selectFrom("users").all().asCql(), Map.class);
|
||||
assertThat(map).containsEntry("user_id", user.getId());
|
||||
assertThat(map).containsEntry("fname", "Walter");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.convert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Currency;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
import com.datastax.oss.driver.api.core.data.TupleValue;
|
||||
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest(classes = ConverterConfiguration.class)
|
||||
class ConversionIntegrationTests {
|
||||
|
||||
@Autowired CassandraOperations operations;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
operations.truncate(Addressbook.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and stores a new {@link Addressbook} inside of Cassandra. {@link Contact} classes are converted using the
|
||||
* custom {@link example.springdata.cassandra.convert.ConverterConfiguration.PersonWriteConverter}.
|
||||
*/
|
||||
@Test
|
||||
void shouldCreateAddressbook() {
|
||||
|
||||
var addressbook = new Addressbook();
|
||||
addressbook.setId("private");
|
||||
|
||||
addressbook.setMe(new Contact("Walter", "White"));
|
||||
addressbook.setFriends(Arrays.asList(new Contact("Jesse", "Pinkman"), new Contact("Saul", "Goodman")));
|
||||
|
||||
operations.insert(addressbook);
|
||||
|
||||
var row = operations.selectOne(QueryBuilder.selectFrom("addressbook").all().asCql(), Row.class);
|
||||
|
||||
assertThat(row).isNotNull();
|
||||
|
||||
assertThat(row.getString("id")).isEqualTo("private");
|
||||
assertThat(row.getString("me")).contains("\"firstname\":\"Walter\"");
|
||||
assertThat(row.getList("friends", String.class)).hasSize(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and loads a new {@link Addressbook} inside of Cassandra. {@link Contact} classes are converted using the
|
||||
* custom {@link example.springdata.cassandra.convert.ConverterConfiguration.PersonReadConverter}.
|
||||
*/
|
||||
@Test
|
||||
void shouldReadAddressbook() {
|
||||
|
||||
var addressbook = new Addressbook();
|
||||
addressbook.setId("private");
|
||||
|
||||
addressbook.setMe(new Contact("Walter", "White"));
|
||||
addressbook.setFriends(Arrays.asList(new Contact("Jesse", "Pinkman"), new Contact("Saul", "Goodman")));
|
||||
|
||||
operations.insert(addressbook);
|
||||
|
||||
var loaded = operations.selectOne(QueryBuilder.selectFrom("addressbook").all().asCql(), Addressbook.class);
|
||||
|
||||
assertThat(loaded.getMe()).isEqualTo(addressbook.getMe());
|
||||
assertThat(loaded.getFriends()).isEqualTo(addressbook.getFriends());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and stores a new {@link Addressbook} inside of Cassandra. The {@link Addressbook} is read back to a
|
||||
* {@link CustomAddressbook} class using the
|
||||
* {@link example.springdata.cassandra.convert.ConverterConfiguration.CustomAddressbookReadConverter}.
|
||||
*/
|
||||
@Test
|
||||
void shouldReadCustomAddressbook() {
|
||||
|
||||
var addressbook = new Addressbook();
|
||||
addressbook.setId("private");
|
||||
|
||||
addressbook.setMe(new Contact("Walter", "White"));
|
||||
|
||||
operations.insert(addressbook);
|
||||
|
||||
var loaded = operations.selectOne(QueryBuilder.selectFrom("addressbook").all().asCql(),
|
||||
CustomAddressbook.class);
|
||||
|
||||
assertThat(loaded.theId()).isEqualTo(addressbook.getId());
|
||||
assertThat(loaded.myDetailsAsJson()).contains("\"firstname\":\"Walter\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and stores a new {@link Addressbook} inside of Cassandra writing map and tuple columns.
|
||||
*/
|
||||
@Test
|
||||
void shouldWriteConvertedMapsAndTuples() {
|
||||
|
||||
var addressbook = new Addressbook();
|
||||
addressbook.setId("private");
|
||||
|
||||
Map<Integer, Currency> preferredCurrencies = new HashMap<>();
|
||||
preferredCurrencies.put(1, Currency.getInstance("USD"));
|
||||
preferredCurrencies.put(2, Currency.getInstance("EUR"));
|
||||
|
||||
var address = new Address("3828 Piermont Dr", "Albuquerque", "87111");
|
||||
|
||||
addressbook.setPreferredCurrencies(preferredCurrencies);
|
||||
addressbook.setAddress(address);
|
||||
|
||||
operations.insert(addressbook);
|
||||
|
||||
var row = operations.selectOne(QueryBuilder.selectFrom("addressbook").all().asCql(), Row.class);
|
||||
|
||||
assertThat(row).isNotNull();
|
||||
|
||||
var tupleValue = row.getTupleValue("address");
|
||||
assertThat(tupleValue.getString(0)).isEqualTo(address.address());
|
||||
assertThat(tupleValue.getString(1)).isEqualTo(address.city());
|
||||
assertThat(tupleValue.getString(2)).isEqualTo(address.zip());
|
||||
|
||||
var rawPreferredCurrencies = row.getMap("preferredCurrencies", Integer.class, String.class);
|
||||
|
||||
assertThat(rawPreferredCurrencies).containsEntry(1, "USD").containsEntry(2, "EUR");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.events;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.query.Query;
|
||||
|
||||
/**
|
||||
* Test showing differences between fetching results as {@link List} and {@link Stream streaming} results using
|
||||
* Cassandra Lifecyle Events.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest(classes = BasicConfiguration.class)
|
||||
@CassandraKeyspace
|
||||
class LifecycleEventsTests {
|
||||
|
||||
@Autowired CassandraOperations operations;
|
||||
|
||||
@Test
|
||||
void shouldStreamEntities() {
|
||||
|
||||
insertEntities();
|
||||
|
||||
var userStream = operations.stream(Query.empty(), User.class);
|
||||
userStream.forEach(System.out::println);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEntitiesAsList() {
|
||||
|
||||
insertEntities();
|
||||
|
||||
var userStream = operations.select(Query.empty(), User.class);
|
||||
userStream.forEach(System.out::println);
|
||||
}
|
||||
|
||||
private void insertEntities() {
|
||||
|
||||
var walter = new User(1, "Walter", "White");
|
||||
var skyler = new User(2, "Skyler", "White");
|
||||
var jesse = new User(3, "Jesse Pinkman", "Jesse Pinkman");
|
||||
|
||||
operations.truncate(User.class);
|
||||
|
||||
operations.insert(walter);
|
||||
operations.insert(skyler);
|
||||
operations.insert(jesse);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.optimisticlocking;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.UpdateOptions;
|
||||
import org.springframework.data.cassandra.core.query.Criteria;
|
||||
|
||||
/**
|
||||
* Integration test showing the basic usage of Optimistic Locking through {@link OptimisticPersonRepository}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest(classes = BasicConfiguration.class)
|
||||
@CassandraKeyspace
|
||||
class OptimisticPersonRepositoryTests {
|
||||
|
||||
@Autowired OptimisticPersonRepository repository;
|
||||
@Autowired CassandraOperations operations;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving an object using the Cassandra Repository will create a persistent representation of the object in Cassandra
|
||||
* and increment the version property.
|
||||
*/
|
||||
@Test
|
||||
void insertShouldIncrementVersion() {
|
||||
|
||||
var person = new OptimisticPerson(42L, 0, "Walter White");
|
||||
|
||||
var saved = repository.save(person);
|
||||
|
||||
assertThat(saved.version()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifying an existing object will update the last modified fields.
|
||||
*/
|
||||
@Test
|
||||
void updateShouldDetectChangedEntity() {
|
||||
|
||||
var person = new OptimisticPerson(42L, 0, "Walter White");
|
||||
|
||||
// Load the person because we intend to change it.
|
||||
var saved = repository.save(person);
|
||||
|
||||
// Another process has changed the person object in the meantime.
|
||||
var anotherProcess = repository.findById(person.id()).get();
|
||||
anotherProcess = anotherProcess.withName("Heisenberg");
|
||||
repository.save(anotherProcess);
|
||||
|
||||
// Now it's our turn to modify the object...
|
||||
var ourSaved = saved.withName("Walter");
|
||||
|
||||
// ...which fails with a OptimisticLockingFailureException, using LWT under the hood.
|
||||
assertThatExceptionOfType(OptimisticLockingFailureException.class).isThrownBy(() -> repository.save(ourSaved));
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests uses lightweight transactions by leveraging mapped {@code IF} conditions with the {@code UPDATE}
|
||||
* statement through {@link CassandraOperations#update(Object, UpdateOptions)}.
|
||||
*/
|
||||
@Test
|
||||
void updateUsingLightWeightTransactions() {
|
||||
|
||||
var person = new SimplePerson();
|
||||
person.setId(42L);
|
||||
person.setName("Walter White");
|
||||
|
||||
operations.insert(person);
|
||||
|
||||
var success = operations.update(person,
|
||||
UpdateOptions.builder().ifCondition(Criteria.where("name").is("Walter White")).build());
|
||||
|
||||
assertThat(success.wasApplied()).isTrue();
|
||||
|
||||
var failed = operations.update(person,
|
||||
UpdateOptions.builder().ifCondition(Criteria.where("name").is("Heisenberg")).build());
|
||||
|
||||
assertThat(failed.wasApplied()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.projection;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.projection.TargetAware;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link CustomerRepository} to show projection capabilities.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest(classes = ProjectionConfiguration.class)
|
||||
@CassandraKeyspace
|
||||
class CustomerRepositoryIntegrationTest {
|
||||
|
||||
@Autowired CustomerRepository customers;
|
||||
|
||||
private Customer dave, carter;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
customers.deleteAll();
|
||||
|
||||
this.dave = customers.save(new Customer("d", "Dave", "Matthews"));
|
||||
this.carter = customers.save(new Customer("c", "Carter", "Beauford"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectsEntityIntoInterface() {
|
||||
|
||||
var result = customers.findAllProjectedBy();
|
||||
|
||||
assertThat(result).hasSize(2);
|
||||
assertThat(result.iterator().next().getFirstname()).isEqualTo("Carter");
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectsDynamically() {
|
||||
|
||||
var result = customers.findById("d", CustomerProjection.class);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.iterator().next().getFirstname()).isEqualTo("Dave");
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectsIndividualDynamically() {
|
||||
|
||||
var result = customers.findProjectedById(dave.id(), CustomerSummary.class);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getFullName()).isEqualTo("Dave Matthews");
|
||||
|
||||
// Proxy backed by original instance as the projection uses dynamic elements
|
||||
assertThat(((TargetAware) result).getTarget()).isInstanceOf(Customer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectIndividualInstance() {
|
||||
|
||||
var result = customers.findProjectedById(dave.id());
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getFirstname()).isEqualTo("Dave");
|
||||
assertThat(((TargetAware) result).getTarget()).isInstanceOf(Customer.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* Integration test to show the usage of JSR-310 date/time types with Spring Data Cassandra.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest(classes = CassandraConfiguration.class)
|
||||
class Jsr310IntegrationTests {
|
||||
|
||||
@Autowired OrderRepository repository;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findOneByJsr310Types() {
|
||||
|
||||
var order = new Order("42", LocalDate.now(), ZoneId.systemDefault());
|
||||
|
||||
repository.save(order);
|
||||
|
||||
assertThat(repository.findOrderByOrderDateAndZoneId(order.getOrderDate(), order.getZoneId())).isEqualTo(order);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.streamoptional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* Integration test to show the usage of Java {@link Stream} and {@link Optional} features with Spring Data Cassandra.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest(classes = CassandraConfiguration.class)
|
||||
class StreamOptionalIntegrationTests {
|
||||
|
||||
@Autowired PersonRepository repository;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
void providesFindOneWithOptional() {
|
||||
|
||||
var homer = repository.save(new Person("1", "Homer", "Simpson"));
|
||||
|
||||
assertThat(repository.findById(homer.id).isPresent()).isTrue();
|
||||
assertThat(repository.findById(homer.id + 1)).isEqualTo(Optional.<Person> empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokesDefaultMethod() {
|
||||
|
||||
var homer = repository.save(new Person("1", "Homer", "Simpson"));
|
||||
var result = repository.findByPerson(homer);
|
||||
|
||||
assertThat(result.isPresent()).isTrue();
|
||||
assertThat(result.get()).isEqualTo(homer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streaming data from the store by using a repository method that returns a {@link Stream}. Note, that since the
|
||||
* resulting {@link Stream} contains state it needs to be closed explicitly after use!
|
||||
*/
|
||||
@Test
|
||||
void useJava8StreamsWithCustomQuery() {
|
||||
|
||||
var homer = repository.save(new Person("1", "Homer", "Simpson"));
|
||||
var bart = repository.save(new Person("2", "Bart", "Simpson"));
|
||||
|
||||
try (var stream = repository.findAll()) {
|
||||
assertThat(stream.collect(Collectors.toList())).contains(homer, bart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.udt;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
|
||||
import org.springframework.data.cassandra.config.SchemaAction;
|
||||
import org.springframework.data.cassandra.core.CassandraAdminOperations;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
|
||||
import com.datastax.oss.driver.api.core.data.UdtValue;
|
||||
import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata;
|
||||
import com.datastax.oss.driver.api.core.type.UserDefinedType;
|
||||
|
||||
/**
|
||||
* Integration test to show User-Defined type support.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SpringBootTest
|
||||
@CassandraKeyspace
|
||||
class UserDefinedTypeIntegrationTest {
|
||||
|
||||
@Configuration
|
||||
static class Config extends AbstractCassandraConfiguration {
|
||||
|
||||
@Override
|
||||
protected int getPort() {
|
||||
return Integer.getInteger("spring.data.cassandra.port");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getContactPoints() {
|
||||
return System.getProperty("spring.data.cassandra.contact-points");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyspaceName() {
|
||||
return "example";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLocalDataCenter() {
|
||||
return "datacenter1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEntityBasePackages() {
|
||||
return new String[] { Person.class.getPackage().getName() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchemaAction getSchemaAction() {
|
||||
return SchemaAction.RECREATE;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired CassandraOperations operations;
|
||||
@Autowired CassandraAdminOperations adminOperations;
|
||||
|
||||
@BeforeEach
|
||||
void before() throws Exception {
|
||||
operations.getCqlOperations().execute("TRUNCATE person");
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a row with a mapped User-defined type.
|
||||
*/
|
||||
@Test
|
||||
void insertMappedUdt() {
|
||||
|
||||
var person = new Person();
|
||||
person.setId(42);
|
||||
person.setFirstname("Walter");
|
||||
person.setLastname("White");
|
||||
|
||||
person.setCurrent(new Address("308 Negra Arroyo Lane", "87104", "Albuquerque"));
|
||||
person.setPrevious(Collections.singletonList(new Address("12000 – 12100 Coors Rd SW", "87045", "Albuquerque")));
|
||||
|
||||
operations.insert(person);
|
||||
|
||||
var loaded = operations.selectOne("SELECT * FROM person WHERE id = 42", Person.class);
|
||||
|
||||
assertThat(loaded.getCurrent()).isEqualTo(person.getCurrent());
|
||||
assertThat(loaded.getPrevious()).containsAll(person.getPrevious());
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a row with a raw User-defined type.
|
||||
*/
|
||||
@Test
|
||||
void insertRawUdt() {
|
||||
|
||||
var keyspaceMetadata = adminOperations.getKeyspaceMetadata();
|
||||
var address = keyspaceMetadata.getUserDefinedType("address").get();
|
||||
|
||||
var udtValue = address.newValue();
|
||||
udtValue.setString("street", "308 Negra Arroyo Lane");
|
||||
udtValue.setString("zip", "87104");
|
||||
udtValue.setString("city", "Albuquerque");
|
||||
|
||||
var person = new Person();
|
||||
person.setId(42);
|
||||
person.setFirstname("Walter");
|
||||
person.setLastname("White");
|
||||
|
||||
person.setAlternative(udtValue);
|
||||
|
||||
operations.insert(person);
|
||||
|
||||
var loaded = operations.selectOne("SELECT * FROM person WHERE id = 42", Person.class);
|
||||
|
||||
assertThat(loaded.getAlternative().getString("zip")).isEqualTo("87104");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Spring Data Cassandra - Kotlin examples
|
||||
|
||||
This project contains samples of Kotlin-specific features of Spring Data (Cassandra).
|
||||
|
||||
## Value defaulting on entity construction
|
||||
|
||||
Kotlin allows defaulting for constructor- and method arguments.
|
||||
Defaulting allows usage of substitute values if a field in the document is absent or simply `null`.
|
||||
Spring Data inspects objects whether they are Kotlin types and uses the appropriate constructor.
|
||||
|
||||
```kotlin
|
||||
@Table
|
||||
data class Person(@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) val firstname: String? = "", val lastname: String = "White")
|
||||
|
||||
operations.cqlOperations.execute(QueryBuilder.insertInto("person").value("firstname", "Walter"))
|
||||
|
||||
val person = operations.query<Person>()
|
||||
.matching(query(where("firstname").isEqualTo("Walter")))
|
||||
.firstValue()!!
|
||||
|
||||
assertThat(person.lastname).isEqualTo("White")
|
||||
```
|
||||
|
||||
## Kotlin Extensions
|
||||
|
||||
Spring Data exposes methods accepting a target type to either query for or to project results values on.
|
||||
Kotlin represents classes with its own type, `KClass` which can be an obstacle when attempting to obtain a Java `Class` type.
|
||||
|
||||
Spring Data ships with extensions that add overloads for methods accepting a type parameter by either leveraging generics or accepting `KClass` directly.
|
||||
|
||||
```kotlin
|
||||
operations.getTableName<Person>()
|
||||
|
||||
operations.getTableName(Person::class)
|
||||
```
|
||||
|
||||
## Nullability
|
||||
|
||||
Declaring repository interfaces using Kotlin allows expressing nullability constraints on arguments and return types. Spring Data evaluates nullability of arguments and return types and reacts to these. Passing `null` to a non-nullable argument raises an `IllegalArgumentException`, as you're already used to from Kotlin. Spring Data helps you also to prevent `null` in query results. If you wish to return a nullable result, use Kotlin's nullability marker `?`. To prevent `null` results, declare the return type of a query method as non-nullable. In the case a query yields no result, a non-nullable query method throws `EmptyResultDataAccessException`.
|
||||
|
||||
```kotlin
|
||||
interface PersonRepository : CrudRepository<Person, String> {
|
||||
|
||||
/**
|
||||
* Query method declaring a nullable return type that allows to return null values.
|
||||
*/
|
||||
fun findOneOrNoneByFirstname(firstname: String): Person?
|
||||
|
||||
/**
|
||||
* Query method declaring a nullable argument.
|
||||
*/
|
||||
fun findNullableByFirstname(firstname: String?): Person?
|
||||
|
||||
/**
|
||||
* Query method requiring a result. Throws [org.springframework.dao.EmptyResultDataAccessException] if no result is found.
|
||||
*/
|
||||
fun findOneByFirstname(firstname: String): Person
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-cassandra-examples</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-cassandra-kotlin</artifactId>
|
||||
<name>Spring Data Cassandra - Kotlin features</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-cassandra-example-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<configuration>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
<compilerPlugins>
|
||||
<plugin>spring</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.kotlin
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
class ApplicationConfiguration
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<ApplicationConfiguration>(*args)
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.kotlin
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.PrimaryKeyType
|
||||
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn
|
||||
import org.springframework.data.cassandra.core.mapping.Table
|
||||
|
||||
/**
|
||||
* An entity to represent a Person.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table
|
||||
data class Person(@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) val firstname: String? = "", val lastname: String = "White")
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.kotlin
|
||||
|
||||
import org.springframework.data.repository.CrudRepository
|
||||
|
||||
/**
|
||||
* Repository interface to manage [Person] instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface PersonRepository : CrudRepository<Person, String> {
|
||||
|
||||
/**
|
||||
* Query method declaring a nullable return type that allows to return null values.
|
||||
*/
|
||||
fun findOneOrNoneByFirstname(firstname: String): Person?
|
||||
|
||||
/**
|
||||
* Query method declaring a nullable argument.
|
||||
*/
|
||||
fun findNullableByFirstname(firstname: String?): Person?
|
||||
|
||||
/**
|
||||
* Query method requiring a result. Throws [org.springframework.dao.EmptyResultDataAccessException] if no result is found.
|
||||
*/
|
||||
fun findOneByFirstname(firstname: String): Person
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
spring.data.cassandra.keyspace-name=example
|
||||
spring.data.cassandra.schema-action=recreate
|
||||
spring.data.cassandra.local-datacenter=datacenter1
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.kotlin
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.dao.EmptyResultDataAccessException
|
||||
|
||||
/**
|
||||
* Tests showing Kotlin usage of Spring Data Repositories.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest
|
||||
class RepositoryTests {
|
||||
|
||||
@Autowired
|
||||
lateinit var repository: PersonRepository
|
||||
|
||||
@BeforeEach
|
||||
fun before() {
|
||||
repository.deleteAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find one person`() {
|
||||
|
||||
repository.save(Person("Walter", "White"))
|
||||
|
||||
val walter = repository.findOneByFirstname("Walter")
|
||||
|
||||
assertThat(walter).isNotNull()
|
||||
assertThat(walter.firstname).isEqualTo("Walter")
|
||||
assertThat(walter.lastname).isEqualTo("White")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return null if no person found`() {
|
||||
|
||||
repository.save(Person("Walter", "White"))
|
||||
|
||||
val walter = repository.findOneOrNoneByFirstname("Hank")
|
||||
|
||||
assertThat(walter).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should throw EmptyResultDataAccessException if no person found`() {
|
||||
|
||||
repository.save(Person("Walter", "White"))
|
||||
|
||||
assertThatThrownBy { repository.findOneByFirstname("Hank") }.isInstanceOf(EmptyResultDataAccessException::class.java)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.kotlin
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier
|
||||
import com.datastax.oss.driver.api.querybuilder.QueryBuilder
|
||||
import example.springdata.cassandra.util.CassandraKeyspace
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.data.cassandra.core.*
|
||||
import org.springframework.data.cassandra.core.query.Query.query
|
||||
import org.springframework.data.cassandra.core.query.isEqualTo
|
||||
import org.springframework.data.cassandra.core.query.where
|
||||
|
||||
/**
|
||||
* Tests showing Kotlin usage of [MongoTemplate] and its Kotlin extensions.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest
|
||||
class TemplateTests {
|
||||
|
||||
@Autowired
|
||||
lateinit var operations: CassandraOperations
|
||||
|
||||
@BeforeEach
|
||||
fun before() {
|
||||
operations.truncate<Person>()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should create collection leveraging reified type parameters`() {
|
||||
assertThat(operations.getTableName<Person>()).isEqualTo(CqlIdentifier.fromCql("person"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should insert and find person in a fluent API style`() {
|
||||
|
||||
operations.insert<Person>().inTable("person").one(Person("Walter", "White"))
|
||||
|
||||
val people = operations.query<Person>()
|
||||
.matching(query(where("firstname").isEqualTo("Walter")))
|
||||
.all()
|
||||
|
||||
assertThat(people).hasSize(1).extracting("firstname").containsOnly("Walter")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should insert and project query results`() {
|
||||
|
||||
operations.insert<Person>().inTable("person").one(Person("Walter", "White"))
|
||||
|
||||
val firstnameOnly = operations.query<Person>()
|
||||
.asType<FirstnameOnly>()
|
||||
.matching(query(where("firstname").isEqualTo("Walter")))
|
||||
.oneValue()
|
||||
|
||||
assertThat(firstnameOnly?.getFirstname()).isEqualTo("Walter")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should insert and count objects in a fluent API style`() {
|
||||
|
||||
operations.insert<Person>().inTable("person").one(Person("Walter", "White"))
|
||||
|
||||
val count = operations.query<Person>()
|
||||
.matching(query(where("firstname").isEqualTo("Walter")))
|
||||
.count()
|
||||
|
||||
assertThat(count).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should insert and find person`() {
|
||||
|
||||
val person = Person("Walter", "White")
|
||||
|
||||
operations.insert(person)
|
||||
|
||||
val people = operations.select<Person>(query(where("firstname").isEqualTo("Walter")))
|
||||
|
||||
assertThat(people).hasSize(1).extracting("firstname").containsOnly("Walter")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should apply defaulting for absent properties`() {
|
||||
|
||||
operations.cqlOperations.execute(QueryBuilder.insertInto("person").value("firstname", QueryBuilder.literal("Walter")).asCql())
|
||||
|
||||
val person = operations.query<Person>()
|
||||
.matching(query(where("firstname").isEqualTo("Walter")))
|
||||
.firstValue()!!
|
||||
|
||||
assertThat(person.firstname).isEqualTo("Walter")
|
||||
assertThat(person.lastname).isEqualTo("White")
|
||||
|
||||
|
||||
val resultSet = operations.cqlOperations.queryForResultSet("SELECT * FROM person WHERE firstname = 'Walter'")
|
||||
val walter = resultSet.one()!!
|
||||
|
||||
assertThat(walter).isNotNull
|
||||
assertThat(walter.getString("firstname")).isEqualTo("Walter")
|
||||
assertThat(walter.getString("lastname")).isNull()
|
||||
}
|
||||
|
||||
interface FirstnameOnly {
|
||||
fun getFirstname(): String
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-cassandra-examples</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-examples</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Cassandra - Examples</name>
|
||||
<description>Sample projects for Spring Data Cassandra</description>
|
||||
<url>https://projects.spring.io/spring-data-cassandra</url>
|
||||
<inceptionYear>2014</inceptionYear>
|
||||
|
||||
<modules>
|
||||
<module>util</module>
|
||||
<module>example</module>
|
||||
<module>kotlin</module>
|
||||
<module>reactive</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-cassandra</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Spring Data Cassandra 2.0 - Reactive examples
|
||||
|
||||
This project contains samples of reactive data access features with Spring Data (Cassandra).
|
||||
|
||||
## Reactive Template API usage with `ReactiveCassandraTemplate`
|
||||
|
||||
The main reactive Template API class is `ReactiveCassandraTemplate`, ideally used through its interface `ReactiveCassandraOperations`. It defines a basic set of reactive data access operations using [Project Reactor](http://projectreactor.io) `Mono` and `Flux` reactive types.
|
||||
|
||||
```java
|
||||
template.insert(Flux.just(new Person("Walter", "White", 50),
|
||||
new Person("Skyler", "White", 45),
|
||||
new Person("Saul", "Goodman", 42),
|
||||
new Person("Jesse", "Pinkman", 27)));
|
||||
|
||||
Flux<Person> flux = template.select(select()
|
||||
.from("person")
|
||||
.where(eq("lastname", "White")), Person.class);
|
||||
```
|
||||
|
||||
The test cases in `ReactiveCassandraTemplateIntegrationTest` show basic Template API usage.
|
||||
Reactive data access reads and converts individual elements while processing the stream.
|
||||
|
||||
|
||||
## Reactive Repository support
|
||||
|
||||
Spring Data Cassandra provides reactive repository support with Project Reactor and RxJava 1 reactive types. The reactive API supports reactive type conversion between reactive types.
|
||||
|
||||
```java
|
||||
public interface ReactivePersonRepository extends ReactiveCrudRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByLastname(String lastname);
|
||||
|
||||
@Query("SELECT * FROM person WHERE firstname = ?0 and lastname = ?1")
|
||||
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
// Accept parameter inside a reactive type for deferred execution
|
||||
Flux<Person> findByLastname(Mono<String> lastname);
|
||||
|
||||
Mono<Person> findByFirstnameAndLastname(Mono<String> firstname, String lastname);
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public interface RxJava1PersonRepository extends RxJava1CrudRepository<Person, String> {
|
||||
|
||||
Observable<Person> findByLastname(String lastname);
|
||||
|
||||
@Query("SELECT * FROM person WHERE firstname = ?0 and lastname = ?1")
|
||||
Single<Person> findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
// Accept parameter inside a reactive type for deferred execution
|
||||
Observable<Person> findByLastname(Single<String> lastname);
|
||||
|
||||
Single<Person> findByFirstnameAndLastname(Single<String> firstname, String lastname);
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-cassandra-examples</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-cassandra-reactive</artifactId>
|
||||
<name>Spring Data Cassandra - Reactive features</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.reactivex.rxjava2</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-cassandra-example-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.cassandra.config.EnableReactiveCassandraAuditing;
|
||||
import org.springframework.data.cassandra.core.CassandraTemplate;
|
||||
import org.springframework.data.cassandra.core.InsertOptions;
|
||||
import org.springframework.data.cassandra.core.cql.CqlTemplate;
|
||||
import org.springframework.data.domain.ReactiveAuditorAware;
|
||||
|
||||
/**
|
||||
* Simple configuration for reactive Cassandra auditing.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableReactiveCassandraAuditing
|
||||
class ApplicationConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveAuditorAware<String> reactiveAuditorAware() {
|
||||
return () -> Mono.just("the-current-user");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
/**
|
||||
* An entity to represent a Person.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Table
|
||||
@RequiredArgsConstructor
|
||||
public class Order implements Persistable<String> {
|
||||
|
||||
@Transient boolean isNew;
|
||||
|
||||
@Id final String orderId;
|
||||
|
||||
@CreatedBy String createdBy;
|
||||
|
||||
@CreatedDate Instant createdDate;
|
||||
|
||||
@LastModifiedBy String lastModifiedBy;
|
||||
|
||||
@LastModifiedDate Instant lastModifiedDate;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return getOrderId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return isNew;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface OrderRepository extends ReactiveCrudRepository<Order, String> {}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Simple configuration for reactive Cassandra support.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
class ApplicationConfiguration {
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
|
||||
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* An entity to represent a Person.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Table
|
||||
public class Person {
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED, ordinal = 2) //
|
||||
private String firstname;
|
||||
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 1) //
|
||||
private String lastname;
|
||||
|
||||
private int age;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
|
||||
/**
|
||||
* Repository interface to manage {@link Person} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface ReactivePersonRepository extends ReactiveCrudRepository<Person, String> {
|
||||
|
||||
/**
|
||||
* Derived query selecting by {@code lastname}.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
Flux<Person> findByLastname(String lastname);
|
||||
|
||||
/**
|
||||
* String query selecting one entity.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
@Query("SELECT * FROM person WHERE firstname = ?0 and lastname = ?1")
|
||||
Mono<Person> findByFirstnameInAndLastname(String firstname, String lastname);
|
||||
|
||||
/**
|
||||
* Derived query selecting by {@code lastname}. {@code lastname} uses deferred resolution that does not require
|
||||
* blocking to obtain the parameter value.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
Flux<Person> findByLastname(Mono<String> lastname);
|
||||
|
||||
/**
|
||||
* Derived query selecting by {@code firstname} and {@code lastname}. {@code firstname} uses deferred resolution that
|
||||
* does not require blocking to obtain the parameter value.
|
||||
*
|
||||
* @param firstname
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
Mono<Person> findByFirstnameAndLastname(Mono<String> firstname, String lastname);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.repository.reactive.RxJava2CrudRepository;
|
||||
|
||||
/**
|
||||
* Repository interface to manage {@link Person} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface RxJava2PersonRepository extends RxJava2CrudRepository<Person, String> {
|
||||
|
||||
/**
|
||||
* Derived query selecting by {@code lastname}.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
Observable<Person> findByLastname(String lastname);
|
||||
|
||||
/**
|
||||
* String query selecting one entity.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
@Query("SELECT * FROM person WHERE firstname = ?0 and lastname = ?1")
|
||||
Single<Person> findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
/**
|
||||
* Derived query selecting by {@code lastname}. {@code lastname} uses deferred resolution that does not require
|
||||
* blocking to obtain the parameter value.
|
||||
*
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
Observable<Person> findByLastname(Single<String> lastname);
|
||||
|
||||
/**
|
||||
* Derived query selecting by {@code firstname} and {@code lastname}. {@code firstname} uses deferred resolution that
|
||||
* does not require blocking to obtain the parameter value.
|
||||
*
|
||||
* @param firstname
|
||||
* @param lastname
|
||||
* @return
|
||||
*/
|
||||
Single<Person> findByFirstnameAndLastname(Single<String> firstname, String lastname);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.spel;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.spel.spi.EvaluationContextExtension;
|
||||
import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension;
|
||||
|
||||
/**
|
||||
* Simple configuration for reactive Cassandra SpEL support.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
class ApplicationConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveTenantExtension tenantExtension() {
|
||||
return ReactiveTenantExtension.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension that looks up a {@link Tenant} from the {@link reactor.util.context.Context}.
|
||||
*/
|
||||
enum ReactiveTenantExtension implements ReactiveEvaluationContextExtension {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Mono<? extends EvaluationContextExtension> getExtension() {
|
||||
return Mono.deferContextual(contextView -> Mono.just(new TenantExtension(contextView.get(Tenant.class))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "my-reactive-tenant-extension";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual extension providing access to the {@link Tenant} value object.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class TenantExtension implements EvaluationContextExtension {
|
||||
|
||||
private final Tenant tenant;
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "my-tenant-extension";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tenant getRootObject() {
|
||||
return tenant;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The root object.
|
||||
*/
|
||||
@Value
|
||||
public static class Tenant {
|
||||
|
||||
String tenantId;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.spel;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
|
||||
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Table
|
||||
public record Employee(@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) String tenantId,
|
||||
@PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED) String name) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.spel;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface EmployeeRepository extends ReactiveCrudRepository<Employee, String> {
|
||||
|
||||
@Query("SELECT * FROM employee WHERE tenantId = :#{getTenantId()} AND name = :name")
|
||||
Flux<Employee> findAllByName(String name);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.auditing;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
|
||||
|
||||
/**
|
||||
* Integration tests showing Reactive Auditing with Cassandra in action.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@DataCassandraTest
|
||||
public class AuditingIntegrationTests {
|
||||
|
||||
@Autowired OrderRepository orderRepository;
|
||||
|
||||
@Test
|
||||
public void shouldUpdateAuditor() throws InterruptedException {
|
||||
|
||||
var order = new Order("4711");
|
||||
order.setNew(true);
|
||||
|
||||
orderRepository.save(order).as(StepVerifier::create).assertNext(actual -> {
|
||||
|
||||
assertThat(actual.getCreatedBy()).isEqualTo("the-current-user");
|
||||
assertThat(actual.getCreatedDate()).isBetween(Instant.now().minusSeconds(60), Instant.now().plusSeconds(60));
|
||||
assertThat(actual.getLastModifiedBy()).isEqualTo("the-current-user");
|
||||
assertThat(actual.getLastModifiedDate()).isBetween(Instant.now().minusSeconds(60), Instant.now().plusSeconds(60));
|
||||
|
||||
}).verifyComplete();
|
||||
|
||||
Thread.sleep(10);
|
||||
|
||||
order = orderRepository.findById("4711").block();
|
||||
|
||||
orderRepository.save(order).as(StepVerifier::create).assertNext(actual -> {
|
||||
|
||||
assertThat(actual.getCreatedBy()).isEqualTo("the-current-user");
|
||||
assertThat(actual.getCreatedDate()).isBefore(actual.getLastModifiedDate());
|
||||
assertThat(actual.getLastModifiedBy()).isEqualTo("the-current-user");
|
||||
assertThat(actual.getLastModifiedDate()).isBetween(Instant.now().minusSeconds(60), Instant.now().plusSeconds(60));
|
||||
|
||||
}).verifyComplete();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import rx.RxReactiveStreams;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraTemplate;
|
||||
|
||||
/**
|
||||
* Integration test for {@link ReactiveCassandraTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest
|
||||
class ReactiveCassandraTemplateIntegrationTest {
|
||||
|
||||
@Autowired ReactiveCassandraTemplate template;
|
||||
|
||||
/**
|
||||
* Truncate table and insert some rows.
|
||||
*/
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
var truncateAndInsert = template.truncate(Person.class) //
|
||||
.thenMany(Flux.just(new Person("Walter", "White", 50), //
|
||||
new Person("Skyler", "White", 45), //
|
||||
new Person("Saul", "Goodman", 42), //
|
||||
new Person("Jesse", "Pinkman", 27))) //
|
||||
.flatMap(template::insert);
|
||||
|
||||
StepVerifier.create(truncateAndInsert).expectNextCount(4).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* This sample performs a count, inserts data and performs a count again using reactive operator chaining. It prints
|
||||
* the two counts ({@code 4} and {@code 6}) to the console.
|
||||
*/
|
||||
@Test
|
||||
void shouldInsertAndCountData() {
|
||||
|
||||
var saveAndCount = template.count(Person.class) //
|
||||
.doOnNext(System.out::println) //
|
||||
.thenMany(Flux.just(new Person("Hank", "Schrader", 43), //
|
||||
new Person("Mike", "Ehrmantraut", 62)))
|
||||
.flatMap(template::insert) //
|
||||
.last() //
|
||||
.flatMap(v -> template.count(Person.class)) //
|
||||
.doOnNext(System.out::println);
|
||||
|
||||
StepVerifier.create(saveAndCount).expectNext(6L).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that the all object conversions are performed before the results are printed to the console.
|
||||
*/
|
||||
@Test
|
||||
void convertReactorTypesToRxJava1() throws Exception {
|
||||
|
||||
var flux = template.select("SELECT * FROM person WHERE lastname = 'White'", Person.class);
|
||||
|
||||
long count = RxReactiveStreams.toObservable(flux) //
|
||||
.count() //
|
||||
.toSingle() //
|
||||
.toBlocking() //
|
||||
.value(); //
|
||||
|
||||
assertThat(count).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* Integration test for {@link ReactivePersonRepository} using Project Reactor types and operators.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@SpringBootTest
|
||||
@CassandraKeyspace
|
||||
class ReactivePersonRepositoryIntegrationTest {
|
||||
|
||||
@Autowired ReactivePersonRepository repository;
|
||||
|
||||
/**
|
||||
* Clear table and insert some rows.
|
||||
*/
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
var deleteAndInsert = repository.deleteAll() //
|
||||
.thenMany(repository.saveAll(Flux.just(new Person("Walter", "White", 50), //
|
||||
new Person("Skyler", "White", 45), //
|
||||
new Person("Saul", "Goodman", 42), //
|
||||
new Person("Jesse", "Pinkman", 27))));
|
||||
|
||||
StepVerifier.create(deleteAndInsert).expectNextCount(4).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* This sample performs a count, inserts data and performs a count again using reactive operator chaining.
|
||||
*/
|
||||
@Test
|
||||
void shouldInsertAndCountData() {
|
||||
|
||||
var saveAndCount = repository.count() //
|
||||
.doOnNext(System.out::println) //
|
||||
.thenMany(repository.saveAll(Flux.just(new Person("Hank", "Schrader", 43), //
|
||||
new Person("Mike", "Ehrmantraut", 62)))) //
|
||||
.last() //
|
||||
.flatMap(v -> repository.count()) //
|
||||
.doOnNext(System.out::println);
|
||||
|
||||
StepVerifier.create(saveAndCount).expectNext(6L).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Result set {@link com.datastax.driver.core.Row}s are converted to entities as they are emitted. Reactive pull and
|
||||
* prefetch define the amount of fetched records.
|
||||
*/
|
||||
@Test
|
||||
void shouldPerformConversionBeforeResultProcessing() {
|
||||
|
||||
StepVerifier.create(repository.findAll().doOnNext(System.out::println)) //
|
||||
.expectNextCount(4) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using query derivation.
|
||||
*/
|
||||
@Test
|
||||
void shouldQueryDataWithQueryDerivation() {
|
||||
StepVerifier.create(repository.findByLastname("White")).expectNextCount(2).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using a string query.
|
||||
*/
|
||||
@Test
|
||||
void shouldQueryDataWithStringQuery() {
|
||||
StepVerifier.create(repository.findByFirstnameInAndLastname("Walter", "White")).expectNextCount(1).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using query derivation.
|
||||
*/
|
||||
@Test
|
||||
void shouldQueryDataWithDeferredQueryDerivation() {
|
||||
StepVerifier.create(repository.findByLastname(Mono.just("White"))).expectNextCount(2).verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using query derivation and deferred parameter resolution.
|
||||
*/
|
||||
@Test
|
||||
void shouldQueryDataWithMixedDeferredQueryDerivation() {
|
||||
|
||||
StepVerifier.create(repository.findByFirstnameAndLastname(Mono.just("Walter"), "White")) //
|
||||
.expectNextCount(1) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
|
||||
/**
|
||||
* Integration test for {@link RxJava2PersonRepository} using RxJava1 types. Note that
|
||||
* {@link ReactiveCassandraOperations} is only available using Project Reactor types as the native Template API
|
||||
* implementation does not come in multiple reactive flavors.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@SpringBootTest
|
||||
public class RxJava2PersonRepositoryIntegrationTest {
|
||||
|
||||
|
||||
@Autowired RxJava2PersonRepository repository;
|
||||
@Autowired ReactiveCassandraOperations operations;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
var deleteAll = repository.deleteAll();
|
||||
|
||||
var save = repository.saveAll(Flowable.just(new Person("Walter", "White", 50), //
|
||||
new Person("Skyler", "White", 45), //
|
||||
new Person("Saul", "Goodman", 42), //
|
||||
new Person("Jesse", "Pinkman", 27)));
|
||||
|
||||
deleteAll.andThen(save).test().await().assertNoErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* This sample performs a count, inserts data and performs a count again using reactive operator chaining. It prints
|
||||
* the two counts ({@code 4} and {@code 6}) to the console.
|
||||
*/
|
||||
@Test
|
||||
public void shouldInsertAndCountData() {
|
||||
|
||||
|
||||
repository.count() //
|
||||
.doOnSuccess(System.out::println) //
|
||||
.toFlowable() //
|
||||
.switchMap(count -> repository.saveAll(Flowable.just(new Person("Hank", "Schrader", 43), //
|
||||
new Person("Mike", "Ehrmantraut", 62)))) //
|
||||
.lastElement() //
|
||||
.toSingle() //
|
||||
.flatMap(v -> repository.count()) //
|
||||
.doOnSuccess(System.out::println) //
|
||||
.test() //
|
||||
.awaitCount(1) //
|
||||
.assertValue(6L) //
|
||||
.assertNoErrors() //
|
||||
.awaitTerminalEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Result set {@link com.datastax.driver.core.Row}s are converted to entities as they are emitted. Reactive pull and
|
||||
* prefetch define the amount of fetched records.
|
||||
*/
|
||||
@Test
|
||||
public void shouldPerformConversionBeforeResultProcessing() {
|
||||
|
||||
repository.findAll() //
|
||||
.doOnNext(System.out::println) //
|
||||
.test() //
|
||||
.awaitCount(4) //
|
||||
.assertNoErrors() //
|
||||
.awaitTerminalEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using query derivation.
|
||||
*/
|
||||
@Test
|
||||
public void shouldQueryDataWithQueryDerivation() {
|
||||
|
||||
repository.findByLastname("White") //
|
||||
.test() //
|
||||
.awaitCount(2) //
|
||||
.assertNoErrors() //
|
||||
.awaitTerminalEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using a string query.
|
||||
*/
|
||||
@Test
|
||||
public void shouldQueryDataWithStringQuery() {
|
||||
|
||||
repository.findByFirstnameAndLastname("Walter", "White") //
|
||||
.test() //
|
||||
.awaitCount(1) //
|
||||
.assertNoErrors() //
|
||||
.awaitTerminalEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using query derivation.
|
||||
*/
|
||||
@Test
|
||||
public void shouldQueryDataWithDeferredQueryDerivation() {
|
||||
|
||||
repository.findByLastname(Single.just("White")) //
|
||||
.test() //
|
||||
.awaitCount(2) //
|
||||
.assertNoErrors() //
|
||||
.awaitTerminalEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data using query derivation and deferred parameter resolution.
|
||||
*/
|
||||
@Test
|
||||
public void shouldQueryDataWithMixedDeferredQueryDerivation() {
|
||||
|
||||
repository.findByFirstnameAndLastname(Single.just("Walter"), "White") //
|
||||
.test() //
|
||||
.awaitCount(1) //
|
||||
.assertNoErrors() //
|
||||
.awaitTerminalEvent();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* Package showing usage of Spring Data Cassandra Reactive Repositories and reactive Cassandra template.
|
||||
*/
|
||||
package example.springdata.cassandra.people;
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.spel;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.cassandra.spel.ApplicationConfiguration.Tenant;
|
||||
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
|
||||
|
||||
/**
|
||||
* Integration tests showing the SpEL context extension in action.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CassandraKeyspace
|
||||
@DataCassandraTest
|
||||
class ExpressionIntegrationTests {
|
||||
|
||||
@Autowired EmployeeRepository employeeRepository;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
|
||||
employeeRepository.deleteAll().as(StepVerifier::create).verifyComplete();
|
||||
|
||||
employeeRepository
|
||||
.saveAll(Arrays.asList(new Employee("breaking-bad", "Walter"), new Employee("breaking-bad", "Hank"),
|
||||
new Employee("south-park", "Hank"))) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextCount(3) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindByTenantIdAndName() {
|
||||
|
||||
employeeRepository.findAllByName("Walter") //
|
||||
.contextWrite(Context.of(Tenant.class, new Tenant("breaking-bad"))).as(StepVerifier::create) //
|
||||
.assertNext(actual -> {
|
||||
assertThat(actual.tenantId()).isEqualTo("breaking-bad");
|
||||
}).verifyComplete();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
spring.data.cassandra.keyspace-name=example
|
||||
spring.data.cassandra.schema-action=recreate
|
||||
spring.data.cassandra.local-datacenter=datacenter1
|
||||
logging.level.org.springframework.data.cassandra=INFO
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-cassandra-examples</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-cassandra-example-utils</artifactId>
|
||||
<name>Spring Data Cassandra - Example Utilities</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>cassandra</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<artifactId>java-driver-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.util;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.platform.commons.util.AnnotationUtils;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import org.testcontainers.containers.CassandraContainer;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
|
||||
/**
|
||||
* JUnit 5 {@link BeforeAllCallback} extension to ensure a running Cassandra server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see CassandraKeyspace
|
||||
*/
|
||||
class CassandraExtension implements BeforeAllCallback {
|
||||
|
||||
private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace
|
||||
.create(CassandraExtension.class);
|
||||
|
||||
private static CassandraContainer container;
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) {
|
||||
|
||||
var store = context.getStore(NAMESPACE);
|
||||
var cassandra = findAnnotation(context);
|
||||
|
||||
var keyspace = store.getOrComputeIfAbsent(CassandraServer.class, it -> {
|
||||
|
||||
CassandraContainer container = runTestcontainer();
|
||||
System.setProperty("spring.data.cassandra.port", "" + container.getMappedPort(9042));
|
||||
System.setProperty("spring.data.cassandra.contact-points", "" + container.getHost());
|
||||
|
||||
return new CassandraServer(container.getHost(), container.getMappedPort(9042),
|
||||
CassandraServer.RuntimeMode.EMBEDDED_IF_NOT_RUNNING);
|
||||
}, CassandraServer.class);
|
||||
|
||||
keyspace.before();
|
||||
|
||||
|
||||
Callable<CqlSession> sessionFactory = () -> CqlSession.builder()
|
||||
.addContactPoint(new InetSocketAddress(keyspace.host(), keyspace.port())).withLocalDatacenter("datacenter1")
|
||||
.build();
|
||||
Awaitility.await().ignoreExceptions().untilAsserted(() -> {
|
||||
|
||||
sessionFactory.call().close();
|
||||
});
|
||||
|
||||
var session = store.getOrComputeIfAbsent(CqlSession.class, it -> {
|
||||
|
||||
try {
|
||||
return sessionFactory.call();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}, CqlSession.class);
|
||||
|
||||
session.execute(String.format("CREATE KEYSPACE IF NOT EXISTS %s \n"
|
||||
+ "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };", cassandra.keyspace()));
|
||||
}
|
||||
|
||||
private static CassandraKeyspace findAnnotation(ExtensionContext context) {
|
||||
|
||||
var testClass = context.getRequiredTestClass();
|
||||
|
||||
var annotation = AnnotationUtils.findAnnotation(testClass, CassandraKeyspace.class);
|
||||
|
||||
return annotation.orElseThrow(() -> new IllegalStateException("Test class not annotated with @Cassandra"));
|
||||
}
|
||||
|
||||
private CassandraContainer<?> runTestcontainer() {
|
||||
|
||||
if (container != null) {
|
||||
return container;
|
||||
}
|
||||
|
||||
container = new CassandraContainer<>(getCassandraDockerImageName());
|
||||
container.withReuse(true);
|
||||
|
||||
container.start();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private String getCassandraDockerImageName() {
|
||||
|
||||
return String.format("cassandra:%s",
|
||||
Optional.ofNullable(System.getenv("CASSANDRA_VERSION")).filter(StringUtils::hasText).orElse("3.11.10"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.util;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
/**
|
||||
* Annotation that can activates embedded Cassandra providing a keyspace at {@link #keyspace()}
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith(CassandraExtension.class)
|
||||
public @interface CassandraKeyspace {
|
||||
|
||||
/**
|
||||
* Name of the desired keyspace to be provided.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String keyspace() default "example";
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.util;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility for Cassandra server use. This utility can start a Cassandra instance, reuse a running instance or simply
|
||||
* require a running Cassandra server (will skip the test if Cassandra is not running).
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
record CassandraServer(String host, int port,
|
||||
example.springdata.cassandra.util.CassandraServer.RuntimeMode runtimeMode) {
|
||||
|
||||
/**
|
||||
* Require a running instance on {@code host:port}. Fails with {@link AssumptionViolatedException} if Cassandra is not
|
||||
* running.
|
||||
*
|
||||
* @param host must not be {@literal null} or empty.
|
||||
* @param port must be between 0 and 65535.
|
||||
* @return the {@link CassandraServer} rule
|
||||
*/
|
||||
public static CassandraServer requireRunningInstance(String host, int port) {
|
||||
return new CassandraServer(host, port, RuntimeMode.REQUIRE_RUNNING_INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an embedded Cassandra instance on {@code host:port} if Cassandra is not running already.
|
||||
*
|
||||
* @param host must not be {@literal null} or empty.
|
||||
* @param port must be between 0 and 65535.
|
||||
* @return the {@link CassandraServer} rule
|
||||
*/
|
||||
public static CassandraServer embeddedIfNotRunning(String host, int port) {
|
||||
return new CassandraServer(host, port, RuntimeMode.EMBEDDED_IF_NOT_RUNNING);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param host must not be {@literal null} or empty.
|
||||
* @param port
|
||||
* @return {@literal true} if the TCP port accepts a connection.
|
||||
*/
|
||||
public static boolean isConnectable(String host, int port) {
|
||||
|
||||
Assert.hasText(host, "Host must not be null or empty!");
|
||||
|
||||
try (var socket = new Socket()) {
|
||||
|
||||
socket.setSoLinger(true, 0);
|
||||
socket.connect(new InetSocketAddress(host, port), (int) TimeUnit.MILLISECONDS.convert(10, TimeUnit.SECONDS));
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
protected void before() {
|
||||
|
||||
if (runtimeMode == RuntimeMode.REQUIRE_RUNNING_INSTANCE) {
|
||||
Assumptions.assumeTrue(isConnectable(getHost(), getPort()),
|
||||
() -> String.format("Cassandra is not reachable at %s:%s.", getHost(), getPort()));
|
||||
}
|
||||
|
||||
if (runtimeMode == RuntimeMode.EMBEDDED_IF_NOT_RUNNING) {
|
||||
if (isConnectable(getHost(), getPort())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum RuntimeMode {
|
||||
REQUIRE_RUNNING_INSTANCE, EMBEDDED_IF_NOT_RUNNING;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.cassandra.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.cql.ResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
/**
|
||||
* Utility to retrieve the Cassandra release version.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@UtilityClass
|
||||
public class CassandraVersion {
|
||||
|
||||
/**
|
||||
* Retrieve the Cassandra release version.
|
||||
*
|
||||
* @param session must not be {@literal null}.
|
||||
* @return the release {@link Version}.
|
||||
*/
|
||||
public static Version getReleaseVersion(CqlSession session) {
|
||||
|
||||
Assert.notNull(session, "Session must not be null");
|
||||
|
||||
var resultSet = session.execute("SELECT release_version FROM system.local;");
|
||||
var row = resultSet.one();
|
||||
|
||||
return Version.parse(row.getString(0));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
# Configuration for the DataStax Java driver for Apache Cassandra®.
|
||||
#
|
||||
# Unless you use a custom mechanism to load your configuration (see
|
||||
# SessionBuilder.withConfigLoader), all the values declared here will be used as defaults. You can
|
||||
# place your own `application.conf` in the classpath to override them.
|
||||
#
|
||||
# Options are classified into two categories:
|
||||
# - basic: what is most likely to be customized first when kickstarting a new application.
|
||||
# - advanced: more elaborate tuning options, or "expert"-level customizations.
|
||||
#
|
||||
# This file is in HOCON format, see https://github.com/typesafehub/config/blob/master/HOCON.md.
|
||||
datastax-java-driver {
|
||||
|
||||
basic.load-balancing-policy {
|
||||
class = DcInferringLoadBalancingPolicy
|
||||
}
|
||||
|
||||
basic.request {
|
||||
# How long the driver waits for a request to complete. This is a global limit on the duration of
|
||||
# a session.execute() call, including any internal retries the driver might do.
|
||||
#
|
||||
# By default, this value is set pretty high to ensure that DDL queries don't time out, in order
|
||||
# to provide the best experience for new users trying the driver with the out-of-the-box
|
||||
# configuration.
|
||||
# For any serious deployment, we recommend that you use separate configuration profiles for DDL
|
||||
# and DML; you can then set the DML timeout much lower (down to a few milliseconds if needed).
|
||||
#
|
||||
# Note that, because timeouts are scheduled on the driver's timer thread, the duration specified
|
||||
# here must be greater than the timer tick duration defined by the
|
||||
# advanced.netty.timer.tick-duration setting (see below). If that is not the case, timeouts will
|
||||
# not be triggered as timely as desired.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for requests issued after the change.
|
||||
# Overridable in a profile: yes
|
||||
timeout = 8 seconds
|
||||
}
|
||||
|
||||
# ADVANCED OPTIONS -------------------------------------------------------------------------------
|
||||
|
||||
advanced.connection {
|
||||
# The timeout to use for internal queries that run as part of the initialization process, just
|
||||
# after we open a connection. If this timeout fires, the initialization of the connection will
|
||||
# fail. If this is the first connection ever, the driver will fail to initialize as well,
|
||||
# otherwise it will retry the connection later.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for connections created after the
|
||||
# change.
|
||||
# Overridable in a profile: no
|
||||
init-query-timeout = 500 milliseconds
|
||||
|
||||
# The driver maintains a connection pool to each node, according to the distance assigned to it
|
||||
# by the load balancing policy. If the distance is IGNORED, no connections are maintained.
|
||||
pool {
|
||||
local {
|
||||
# The number of connections in the pool.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes; when the change is detected, all active pools will be notified
|
||||
# and will adjust their size.
|
||||
# Overridable in a profile: no
|
||||
size = 1
|
||||
}
|
||||
remote {
|
||||
size = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
advanced.metrics {
|
||||
# The session-level metrics (all disabled by default).
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
session {
|
||||
enabled = []
|
||||
|
||||
}
|
||||
# The node-level metrics (all disabled by default).
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
node {
|
||||
enabled = []
|
||||
}
|
||||
}
|
||||
|
||||
advanced.control-connection {
|
||||
schema-agreement {
|
||||
# The interval between each attempt.
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for checks issued after the change.
|
||||
# Overridable in a profile: no
|
||||
interval = 100 seconds
|
||||
|
||||
# The timeout after which schema agreement fails.
|
||||
# If this is set to 0, schema agreement is skipped and will always fail.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for checks issued after the change.
|
||||
# Overridable in a profile: no
|
||||
timeout = 100 seconds
|
||||
|
||||
# Whether to log a warning if schema agreement fails.
|
||||
# You might want to change this if you've set the timeout to 0.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for checks issued after the change.
|
||||
# Overridable in a profile: no
|
||||
warn-on-failure = true
|
||||
}
|
||||
}
|
||||
|
||||
# Options related to the Netty event loop groups used internally by the driver.
|
||||
advanced.netty {
|
||||
|
||||
# Whether the threads created by the driver should be daemon threads.
|
||||
# This will apply to the threads in io-group, admin-group, and the timer thread.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
daemon = false
|
||||
|
||||
# The event loop group used for I/O operations (reading and writing to Cassandra nodes).
|
||||
# By default, threads in this group are named after the session name, "-io-" and an incrementing
|
||||
# counter, for example "s0-io-0".
|
||||
io-group {
|
||||
# The number of threads.
|
||||
# If this is set to 0, the driver will use `Runtime.getRuntime().availableProcessors() * 2`.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
size = 4
|
||||
|
||||
# The options to shut down the event loop group gracefully when the driver closes. If a task
|
||||
# gets submitted during the quiet period, it is accepted and the quiet period starts over.
|
||||
# The timeout limits the overall shutdown time.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
shutdown {quiet-period = 0, timeout = 0, unit = SECONDS}
|
||||
}
|
||||
# The event loop group used for admin tasks not related to request I/O (handle cluster events,
|
||||
# refresh metadata, schedule reconnections, etc.)
|
||||
# By default, threads in this group are named after the session name, "-admin-" and an
|
||||
# incrementing counter, for example "s0-admin-0".
|
||||
admin-group {
|
||||
size = 2
|
||||
|
||||
shutdown {quiet-period = 0, timeout = 0, unit = SECONDS}
|
||||
}
|
||||
}
|
||||
|
||||
advanced.metadata {
|
||||
# Topology events are external signals that inform the driver of the state of Cassandra nodes
|
||||
# (by default, they correspond to gossip events received on the control connection).
|
||||
# The debouncer helps smoothen out oscillations if conflicting events are sent out in short
|
||||
# bursts.
|
||||
# Debouncing may be disabled by setting the window to 0 or max-events to 1 (this is not
|
||||
# recommended).
|
||||
topology-event-debouncer {
|
||||
# How long the driver waits to propagate an event. If another event is received within that
|
||||
# time, the window is reset and a batch of accumulated events will be delivered.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
window = 0 second
|
||||
|
||||
# The maximum number of events that can accumulate. If this count is reached, the events are
|
||||
# delivered immediately and the time window is reset. This avoids holding events indefinitely
|
||||
# if the window keeps getting reset.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
max-events = 20
|
||||
}
|
||||
|
||||
# Options relating to schema metadata (Cluster.getMetadata.getKeyspaces).
|
||||
# This metadata is exposed by the driver for informational purposes, and is also necessary for
|
||||
# token-aware routing.
|
||||
schema {
|
||||
# Whether schema metadata is enabled.
|
||||
# If this is false, the schema will remain empty, or to the last known value.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for refreshes issued after the
|
||||
# change. It can also be overridden programmatically via Cluster.setSchemaMetadataEnabled.
|
||||
# Overridable in a profile: no
|
||||
enabled = true
|
||||
|
||||
# Protects against bursts of schema updates (for example when a client issues a sequence of
|
||||
# DDL queries), by coalescing them into a single update.
|
||||
# Debouncing may be disabled by setting the window to 0 or max-events to 1 (this is highly
|
||||
# discouraged for schema refreshes).
|
||||
debouncer {
|
||||
# How long the driver waits to apply a refresh. If another refresh is requested within that
|
||||
# time, the window is reset and a single refresh will be triggered when it ends.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
window = 0 second
|
||||
|
||||
# The maximum number of refreshes that can accumulate. If this count is reached, a refresh
|
||||
# is done immediately and the window is reset.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: no
|
||||
# Overridable in a profile: no
|
||||
max-events = 20
|
||||
}
|
||||
}
|
||||
|
||||
# Whether token metadata (Cluster.getMetadata.getTokenMap) is enabled.
|
||||
# This metadata is exposed by the driver for informational purposes, and is also necessary for
|
||||
# token-aware routing.
|
||||
# If this is false, it will remain empty, or to the last known value. Note that its computation
|
||||
# requires information about the schema; therefore if schema metadata is disabled or filtered to
|
||||
# a subset of keyspaces, the token map will be incomplete, regardless of the value of this
|
||||
# property.
|
||||
#
|
||||
# Required: yes
|
||||
# Modifiable at runtime: yes, the new value will be used for refreshes issued after the change.
|
||||
# Overridable in a profile: no
|
||||
token-map.enabled = true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.data" level="info" />
|
||||
<logger name="org.apache.cassandra" level="error" />
|
||||
<logger name="com.datastax.driver.core" level="error" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Spring Data Couchbase - Examples
|
||||
|
||||
This project contains samples of data access features with Spring Data (Couchbase).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The examples require a running [Couchbase Server](https://www.couchbase.com/downloads) server with the travel sample bucket imported. We assume you're running Couchbase 6.5 and we have updated the `application.properties` class accordingly to adapt RBAC authentication.
|
||||
|
||||
For more information, see the [official documentation](https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#reference).
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>spring-data-couchbase-examples</artifactId>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-couchbase-example</artifactId>
|
||||
<name>Basic sample for Spring Data Couchbase</name>
|
||||
<description>Small sample project showing the usage of Spring Data Couchbase.</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-couchbase-example-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.couchbase.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.mapping.Field;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
* A domain object representing an Airline
|
||||
*
|
||||
* @author Chandana Kithalagama
|
||||
*/
|
||||
@Data
|
||||
@Document
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Airline {
|
||||
|
||||
private @Id String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String iata;
|
||||
|
||||
private String icao;
|
||||
|
||||
private String callsign;
|
||||
|
||||
private String country;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue