Compare commits
216 commits
gburd/sess
...
develop
Author | SHA1 | Date | |
---|---|---|---|
|
9db3500e3a | ||
|
d179539a31 | ||
|
06fe21d08e | ||
|
099fd999ee | ||
|
662a697d03 | ||
b449817659 | |||
|
e6051b12e9 | ||
|
9f511cde74 | ||
|
4b9187ebe5 | ||
|
93a81e7fd0 | ||
|
3169d0c100 | ||
|
b27bc7d9a9 | ||
|
1f4c2154e2 | ||
|
6788cea1a0 | ||
|
9d94e865b6 | ||
|
654f4434bf | ||
|
6c245c121e | ||
|
ef455ac032 | ||
|
b1e333009c | ||
|
ca6afc326c | ||
|
af4156079d | ||
|
b023ec359b | ||
|
76b603f3d3 | ||
|
d69d8a3b1e | ||
|
6858cf6f48 | ||
5215749de1 | |||
|
2299939be3 | ||
|
0ddacec354 | ||
|
287e1a5b8b | ||
|
8b9d582fa5 | ||
|
96a8476fd8 | ||
|
f168b33f6a | ||
|
11de7015c2 | ||
|
f9b1563bdd | ||
|
e2f45f82c9 | ||
|
27dd9a4eff | ||
|
26f41dab75 | ||
|
1da822ce57 | ||
|
1ef50ae179 | ||
|
26c67e391a | ||
|
3554b7ecb5 | ||
|
7b4e46431f | ||
|
60b040e7a9 | ||
|
7a470bd5d7 | ||
|
0827291253 | ||
|
50f656bc8a | ||
|
9df97b3e44 | ||
|
33b4b35912 | ||
|
1eccb631f3 | ||
|
e932d0dcf2 | ||
|
618a7ea380 | ||
|
7a56059036 | ||
|
33d2459538 | ||
|
a63a1be4b6 | ||
|
d30361538c | ||
|
a993af6c29 | ||
|
39a8643103 | ||
|
c025dc35a7 | ||
|
d19a9c741d | ||
|
6ff188f241 | ||
|
2f0801d36f | ||
|
b4dca9c710 | ||
|
41e5d8c1e5 | ||
|
5570a97dff | ||
|
a198989a76 | ||
|
eb22e3c72e | ||
|
a79e7dacf1 | ||
|
fe47531984 | ||
|
a600c0bd23 | ||
|
b777e9c051 | ||
|
0b86d33725 | ||
|
690cd1e064 | ||
|
ef4f9054ac | ||
|
465c7d6f2a | ||
|
864c4af5af | ||
|
d1fe54b0ce | ||
|
962145bf46 | ||
|
e4cda1a268 | ||
|
377191f12a | ||
|
792d2b6598 | ||
|
2ee300e420 | ||
|
13eaa7e7ea | ||
|
5905663c58 | ||
|
857eadff45 | ||
|
e1884cf52d | ||
|
09a7fbc405 | ||
|
01a458a7f6 | ||
|
48545c1e84 | ||
|
dcea254fdb | ||
|
5605824886 | ||
|
c3f9b83770 | ||
|
52dab5872c | ||
|
a65b775faa | ||
|
62be0e6ccd | ||
|
7b14eda9b3 | ||
|
92f74e41e1 | ||
|
b04e033bf4 | ||
|
c7e37acc5a | ||
|
e5918cd1e8 | ||
|
7535e9ade7 | ||
|
8a7dbfdec1 | ||
|
18f2a057de | ||
|
1642f09ce9 | ||
|
c6b95f12b4 | ||
|
a1e29c62c8 | ||
|
25eb81219d | ||
|
a3b9ff9af3 | ||
|
7c683acb56 | ||
|
d25061366b | ||
|
852ee59da2 | ||
|
682a1a304e | ||
|
fc83b8032c | ||
|
ecd3d71e47 | ||
|
dc9c228e4a | ||
|
e36dded9d2 | ||
|
63c558581b | ||
|
9bbb6d2371 | ||
|
9d91436cb5 | ||
|
eb44912679 | ||
|
e3ec76e6da | ||
|
b40dc37704 | ||
|
1746691826 | ||
|
6e687a7e90 | ||
|
75f32eb542 | ||
|
f64d5edd7c | ||
|
32b06e1494 | ||
|
b82bd8ed75 | ||
|
0dba5d6c53 | ||
|
896748a2ec | ||
|
d369a5b862 | ||
|
74832a32dd | ||
|
2302b958e7 | ||
|
3ef4ddd5a0 | ||
|
237adb40bf | ||
|
4e461c1be0 | ||
|
646b2fa57d | ||
|
605529979d | ||
|
635e2256b0 | ||
|
7156d733fa | ||
|
265c35c4c6 | ||
|
953b47036c | ||
|
1618405c83 | ||
|
828c456814 | ||
|
01102e2299 | ||
|
0e9d1086ed | ||
|
1c37d817b6 | ||
|
306c9e68ef | ||
|
25c5c6b969 | ||
|
be8d1bf029 | ||
|
0c6cda1db6 | ||
|
bf1d5f217f | ||
|
7e6863fc84 | ||
|
265947586c | ||
|
b370272c3a | ||
|
efa87b2d4f | ||
|
16af6ea175 | ||
|
5c8a0360a2 | ||
|
d3c24b70bf | ||
|
844ebd9155 | ||
|
a7094abdfa | ||
|
ac7db5f243 | ||
|
67dbb36194 | ||
|
f9ab9f91a1 | ||
|
895921c598 | ||
|
97f74776d5 | ||
|
8c165a689b | ||
|
9e3fabb2f9 | ||
|
90e1aafd94 | ||
|
efca86783c | ||
|
66578d77d5 | ||
|
75aff52312 | ||
|
83ef8d7b0c | ||
|
58b29ad181 | ||
|
1b46ee0ed1 | ||
|
cb09324ac6 | ||
|
5089be2c22 | ||
|
3cd889ea61 | ||
|
09b06f4ca4 | ||
|
c35d6d19d1 | ||
|
7ac9288eb8 | ||
|
84bb6b5fae | ||
|
933526b05b | ||
|
28aa3b1bae | ||
|
0fd9ff828c | ||
|
94ce778451 | ||
|
142688a215 | ||
|
6ad99fc459 | ||
|
602e3521b4 | ||
|
007b6a51ae | ||
|
9c4d14cc93 | ||
|
1c68ebdcb8 | ||
|
89303f9179 | ||
|
ec55c67d17 | ||
|
7f5cc4add6 | ||
|
90e19907d2 | ||
|
cf2740337e | ||
|
8569eaa76f | ||
|
a126607c09 | ||
|
871c8d0c90 | ||
|
5b0311c88a | ||
|
fd99b60913 | ||
|
62e2e75dfc | ||
|
fa39908f4c | ||
|
253816bd8c | ||
|
512cb0f608 | ||
|
c6cadf345b | ||
|
f008a33e53 | ||
|
2ebae2aaef | ||
|
cbc246f1c0 | ||
|
b9600ac931 | ||
|
efb26b8618 | ||
|
71e84da3bd | ||
|
f4dbf34920 | ||
|
c42803b964 | ||
|
7497cf5a18 | ||
|
b44c898682 |
316 changed files with 20569 additions and 13366 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,3 +1,8 @@
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
gradle/
|
||||||
|
gradlew
|
||||||
|
gradlew.bat
|
||||||
*.iml
|
*.iml
|
||||||
.idea
|
.idea
|
||||||
infer-out
|
infer-out
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<component name="EclipseCodeFormatterProjectSettings">
|
<component name="EclipseCodeFormatterProjectSettings">
|
||||||
<option name="projectSpecificProfile">
|
<option name="projectSpecificProfile">
|
||||||
<ProjectSpecificProfile>
|
<ProjectSpecificProfile>
|
||||||
<option name="formatter" value="ECLIPSE" />
|
|
||||||
<option name="pathToConfigFileJava" value="$PROJECT_DIR$/../newton/formatting/onshape-eclipse-general-preferences.epf" />
|
<option name="pathToConfigFileJava" value="$PROJECT_DIR$/../newton/formatting/onshape-eclipse-general-preferences.epf" />
|
||||||
</ProjectSpecificProfile>
|
</ProjectSpecificProfile>
|
||||||
</option>
|
</option>
|
||||||
|
|
202
NOTES
Normal file
202
NOTES
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
Operation/
|
||||||
|
|-- AbstractStatementOperation
|
||||||
|
| |-- AbstractOperation
|
||||||
|
| | |-- AbstractFilterOperation
|
||||||
|
| | | |-- CountOperation
|
||||||
|
| | | |-- DeleteOperation
|
||||||
|
| | | `-- UpdateOperation
|
||||||
|
| | |-- BoundOperation
|
||||||
|
| | `-- InsertOperation
|
||||||
|
| |-- AbstractOptionalOperation
|
||||||
|
| | |-- AbstractFilterOptionalOperation
|
||||||
|
| | | |-- SelectFirstOperation
|
||||||
|
| | | `-- SelectFirstTransformingOperation
|
||||||
|
| | `-- BoundOptionalOperation
|
||||||
|
| `-- AbstractStreamOperation
|
||||||
|
| |-- AbstractFilterStreamOperation
|
||||||
|
| | |-- SelectOperation
|
||||||
|
| | `-- SelectTransformingOperation
|
||||||
|
| `-- BoundStreamOperation
|
||||||
|
|-- PreparedOperation
|
||||||
|
|-- PreparedOptionalOperation
|
||||||
|
`-- PreparedStreamOperation
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
@CompoundIndex()
|
||||||
|
create a new col in the same table called __idx_a_b_c that the hash of the concatenated values in that order is stored, create a normal index for that (CREATE INDEX ...)
|
||||||
|
if a query matches that set of columns then use that indexed col to fetch the desired results from that table
|
||||||
|
could also work with .in() query if materialized view exists
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
// TODO(gburd): create a statement that matches one that wasn't prepared
|
||||||
|
//String key =
|
||||||
|
// "use " + preparedStatement.getQueryKeyspace() + "; " + preparedStatement.getQueryString();
|
||||||
|
//for (Object param : params) {
|
||||||
|
// key = key.replaceFirst(Pattern.quote("?"), param.toString());
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
primitive types have default values, (e.g. boolean, int, ...) but primative wrapper classes do not and can be null (e.g. Boolean, Integer, ...)
|
||||||
|
|
||||||
|
create table wal {
|
||||||
|
id timeuuid,
|
||||||
|
follows timeuuid,
|
||||||
|
read <Counting Quotient Filter, Set<{keyspace, col, schema generation, timestamp}>>
|
||||||
|
write <Counting Quotient Filter, Set<{keyspace, col, schema generation, timestamp}>>
|
||||||
|
primary key (id, follows)
|
||||||
|
}
|
||||||
|
begin:
|
||||||
|
- insert into wal (timeuuid, parent timeuuid,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: Update operations have no meaning when they only contain primary key components, so
|
||||||
|
// given that `properties` is ordered with the keys first if we find that the last element
|
||||||
|
// is either a partition key or clustering column then we know we should just skip this operation.
|
||||||
|
ColumnType ct = ((HelenusProperty) properties.toArray()[properties.size() - 1]).getColumnType();
|
||||||
|
if (ct != ColumnType.PARTITION_KEY && ct != ColumnType.CLUSTERING_COLUMN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Stream<E> sync() {
|
||||||
|
ListenableFuture<Stream<E>> future = async();
|
||||||
|
Futures.addCallback(future, new FutureCallback<String>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String contents) {
|
||||||
|
//...process web site contents
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable throwable) {
|
||||||
|
log.error("Exception in task", throwable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
-------
|
||||||
|
private mergeCache(Map<String, Set<Object>>
|
||||||
|
|
||||||
|
private static <E> Iterable<E> concat(
|
||||||
|
Iterable<? extends E> i1,
|
||||||
|
Iterable<? extends E> i2) {
|
||||||
|
return new Iterable<E>() {
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return new Iterator<E>() {
|
||||||
|
Iterator<? extends E> listIterator = i1.iterator();
|
||||||
|
Boolean checkedHasNext;
|
||||||
|
E nextValue;
|
||||||
|
private boolean startTheSecond;
|
||||||
|
|
||||||
|
void theNext() {
|
||||||
|
if (listIterator.hasNext()) {
|
||||||
|
checkedHasNext = true;
|
||||||
|
nextValue = listIterator.next();
|
||||||
|
} else if (startTheSecond)
|
||||||
|
checkedHasNext = false;
|
||||||
|
else {
|
||||||
|
startTheSecond = true;
|
||||||
|
listIterator = i2.iterator();
|
||||||
|
theNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (checkedHasNext == null)
|
||||||
|
theNext();
|
||||||
|
return checkedHasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E next() {
|
||||||
|
if (!hasNext())
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
checkedHasNext = null;
|
||||||
|
return nextValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
listIterator.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
----------------------------------
|
||||||
|
if ("ttl".equals(methodName) && method.getParameterCount() == 1 && method.getReturnType() == int.class) {
|
||||||
|
Getter getter = (Getter) args[0];
|
||||||
|
if (getter == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
|
||||||
|
String getterName = prop.getPropertyName();
|
||||||
|
String ttlKeyForProperty = prop.getColumnName().toCql() + "_ttl";
|
||||||
|
if (src.containsKey(ttlKeyForProperty)) {
|
||||||
|
return src.get(ttlKeyForProperty);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("written".equals(methodName) && method.getParameterCount() == 1 && method.getReturnType() == int.class) {
|
||||||
|
Getter getter = (Getter) args[0];
|
||||||
|
if (getter == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
|
||||||
|
String getterName = prop.getPropertyName();
|
||||||
|
String ttlKeyForProperty = prop.getColumnName().toCql() + "_ttl";
|
||||||
|
if (src.containsKey(ttlKeyForProperty)) {
|
||||||
|
return src.get(ttlKeyForProperty);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
|
||||||
|
/*else {
|
||||||
|
Cache<String, Object> cache = session.getSessionCache();
|
||||||
|
Map<String, Object> rowMap = this.cache.rowMap();
|
||||||
|
for (String rowKey : rowMap.keySet()) {
|
||||||
|
String keys = flattenFacets(facets);
|
||||||
|
for (String key : keys) {
|
||||||
|
Object value = cache.getIfPresent(key);
|
||||||
|
if (value != null) {
|
||||||
|
result = Optional.of(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache.put
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
------------------
|
||||||
|
|
||||||
|
InsertOperation
|
||||||
|
|
||||||
|
|
||||||
|
Class<?> iface = entity.getMappingInterface();
|
||||||
|
boolean includesNonIdentityValues = values.stream().map(t -> {
|
||||||
|
ColumnType type = t._1.getProperty().getColumnType();
|
||||||
|
return !((type == ColumnType.PARTITION_KEY) || (type == ColumnType.CLUSTERING_COLUMN));
|
||||||
|
})
|
||||||
|
.reduce(false, (acc, t) -> acc || t);
|
||||||
|
if (resultType == iface) {
|
||||||
|
if (values.size() > 0 && includesNonIdentityValues) {
|
||||||
|
boolean immutable = iface.isAssignableFrom(Drafted.class);
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
final Object value;
|
||||||
|
if (method.getParameterCount() == 1 && args[0] instanceof Boolean && src instanceof ValueProviderMap) {
|
||||||
|
value = ((ValueProviderMap)src).get(methodName, (Boolean)args[0]);
|
||||||
|
} else {
|
||||||
|
value = src.get(methodName);
|
||||||
|
}
|
||||||
|
--------------------
|
27
README.md
27
README.md
|
@ -1,5 +1,5 @@
|
||||||
# Helenus
|
# Helenus
|
||||||
Fast and easy, functional style cutting edge Java 8 and Scala 2.11 Cassandra client for C* 3.x
|
Fast and easy, functional style cutting edge Java 8 Cassandra client for C* 3.x
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
@ -9,14 +9,13 @@ Fast and easy, functional style cutting edge Java 8 and Scala 2.11 Cassandra cli
|
||||||
* Reactive asynchronous and synchronous API
|
* Reactive asynchronous and synchronous API
|
||||||
* Provides Java mapping for Tables, Tuples, UDTs (User Defined Type), Collections, UDT Collections, Tuple Collections
|
* Provides Java mapping for Tables, Tuples, UDTs (User Defined Type), Collections, UDT Collections, Tuple Collections
|
||||||
* Uses lazy mapping in all cases where possible
|
* Uses lazy mapping in all cases where possible
|
||||||
* Supports Guava ListenableFuture and Scala Future
|
* Supports Java 8 Futures and Guava ListenableFuture
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
* JVM 8
|
* JVM 8
|
||||||
* Datastax Driver 3.x
|
* Datastax Driver 3.x
|
||||||
* Cassandra 3.x
|
* Cassandra 3.x
|
||||||
* Scala 2.11+
|
|
||||||
* Maven
|
* Maven
|
||||||
|
|
||||||
### Maven
|
### Maven
|
||||||
|
@ -32,27 +31,6 @@ Latest release dependency:
|
||||||
</dependencies>
|
</dependencies>
|
||||||
```
|
```
|
||||||
|
|
||||||
Active development dependency for Scala 2.11:
|
|
||||||
```
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.helenus</groupId>
|
|
||||||
<artifactId>helenus-core</artifactId>
|
|
||||||
<version>1.2.0_2.11-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>oss-sonatype</id>
|
|
||||||
<name>oss-sonatype</name>
|
|
||||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Simple Example
|
### Simple Example
|
||||||
|
|
||||||
|
@ -132,7 +110,6 @@ public interface AbstractRepository {
|
||||||
|
|
||||||
Account repository:
|
Account repository:
|
||||||
```
|
```
|
||||||
import scala.concurrent.Future;
|
|
||||||
|
|
||||||
public interface AccountRepository extends AbstractRepository {
|
public interface AccountRepository extends AbstractRepository {
|
||||||
|
|
||||||
|
|
3
bin/deploy.sh
Executable file
3
bin/deploy.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
mvn clean jar:jar javadoc:jar source:jar deploy -Prelease
|
14
bin/format.sh
Executable file
14
bin/format.sh
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ "X$1" == "Xall" ]; then
|
||||||
|
for f in $(find ./src -name \*.java); do
|
||||||
|
echo Formatting $f
|
||||||
|
java -jar ./lib/google-java-format-1.3-all-deps.jar --replace $f
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for file in $(git status --short | awk '{print $2}'); do
|
||||||
|
echo $file
|
||||||
|
java -jar ./lib/google-java-format-1.3-all-deps.jar --replace $file
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
mvn clean jar:jar javadoc:jar source:jar install -Prelease
|
mvn clean jar:jar javadoc:jar source:jar install -Prelease
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
mvn clean jar:jar javadoc:jar source:jar deploy -Prelease
|
|
|
@ -11,10 +11,7 @@
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="Maven: com.github.ben-manes.caffeine:caffeine:2.5.3" level="project" />
|
<orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-core:3.3.2" level="project" />
|
||||||
<orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.3" level="project" />
|
|
||||||
<orderEntry type="library" name="Maven: org.scala-lang:scala-library:2.13.0-M1" level="project" />
|
|
||||||
<orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-core:3.3.0" level="project" />
|
|
||||||
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.0.47.Final" level="project" />
|
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.0.47.Final" level="project" />
|
||||||
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.0.47.Final" level="project" />
|
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.0.47.Final" level="project" />
|
||||||
<orderEntry type="library" name="Maven: io.netty:netty-common:4.0.47.Final" level="project" />
|
<orderEntry type="library" name="Maven: io.netty:netty-common:4.0.47.Final" level="project" />
|
||||||
|
@ -31,14 +28,15 @@
|
||||||
<orderEntry type="library" name="Maven: com.github.jnr:jnr-x86asm:1.0.2" level="project" />
|
<orderEntry type="library" name="Maven: com.github.jnr:jnr-x86asm:1.0.2" level="project" />
|
||||||
<orderEntry type="library" name="Maven: com.github.jnr:jnr-posix:3.0.27" level="project" />
|
<orderEntry type="library" name="Maven: com.github.jnr:jnr-posix:3.0.27" level="project" />
|
||||||
<orderEntry type="library" name="Maven: com.github.jnr:jnr-constants:0.9.0" level="project" />
|
<orderEntry type="library" name="Maven: com.github.jnr:jnr-constants:0.9.0" level="project" />
|
||||||
<orderEntry type="library" name="Maven: org.aspectj:aspectjrt:1.8.10" level="project" />
|
<orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-extras:3.3.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: com.diffplug.durian:durian:3.4.0" level="project" />
|
||||||
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.8.10" level="project" />
|
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.8.10" level="project" />
|
||||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.6" level="project" />
|
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.6" level="project" />
|
||||||
<orderEntry type="library" name="Maven: org.springframework:spring-core:4.3.10.RELEASE" level="project" />
|
<orderEntry type="library" name="Maven: org.springframework:spring-core:4.3.10.RELEASE" level="project" />
|
||||||
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
|
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: javax.cache:cache-api:1.1.0" level="project" />
|
||||||
<orderEntry type="library" name="Maven: com.google.guava:guava:20.0" level="project" />
|
<orderEntry type="library" name="Maven: com.google.guava:guava:20.0" level="project" />
|
||||||
<orderEntry type="library" name="Maven: com.github.ben-manes.caffeine:caffeine:2.5.3" level="project" />
|
<orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.2" level="project" />
|
||||||
<orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.3" level="project" />
|
|
||||||
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.0.CR3" level="project" />
|
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.0.CR3" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: com.anthemengineering.mojo:infer-maven-plugin:0.1.0" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: com.anthemengineering.mojo:infer-maven-plugin:0.1.0" level="project" />
|
||||||
|
@ -114,10 +112,11 @@
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.fusesource:sigar:1.6.4" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.fusesource:sigar:1.6.4" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jdt.core.compiler:ecj:4.4.2" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jdt.core.compiler:ecj:4.4.2" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.caffinitas.ohc:ohc-core:0.4.4" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.caffinitas.ohc:ohc-core:0.4.4" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: com.github.ben-manes.caffeine:caffeine:2.2.6" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.jctools:jctools-core:1.2.1" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.jctools:jctools-core:1.2.1" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: ca.exprofesso:guava-jcache:1.0.4" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: commons-io:commons-io:2.5" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: commons-io:commons-io:2.5" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: com.github.stephenc:jamm:0.2.5" level="project" />
|
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-library:1.3" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-library:1.3" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:2.8.47" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:2.8.47" level="project" />
|
||||||
|
|
BIN
lib/google-java-format-1.3-all-deps.jar
Normal file
BIN
lib/google-java-format-1.3-all-deps.jar
Normal file
Binary file not shown.
65
pom.xml
65
pom.xml
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>net.helenus</groupId>
|
<groupId>net.helenus</groupId>
|
||||||
<artifactId>helenus-core</artifactId>
|
<artifactId>helenus-net-core</artifactId>
|
||||||
<version>2.0.5-SNAPSHOT</version>
|
<version>2.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>helenus</name>
|
<name>helenus</name>
|
||||||
|
@ -40,6 +40,9 @@
|
||||||
<properties>
|
<properties>
|
||||||
<dist.id>helenus</dist.id>
|
<dist.id>helenus</dist.id>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
@ -104,21 +107,21 @@
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.scala-lang</groupId>
|
<groupId>com.datastax.cassandra</groupId>
|
||||||
<artifactId>scala-library</artifactId>
|
<artifactId>cassandra-driver-core</artifactId>
|
||||||
<version>2.13.0-M1</version>
|
<version>3.3.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.datastax.cassandra</groupId>
|
<groupId>com.datastax.cassandra</groupId>
|
||||||
<artifactId>cassandra-driver-core</artifactId>
|
<artifactId>cassandra-driver-extras</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>3.3.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.aspectj</groupId>
|
<groupId>com.diffplug.durian</groupId>
|
||||||
<artifactId>aspectjrt</artifactId>
|
<artifactId>durian</artifactId>
|
||||||
<version>1.8.10</version>
|
<version>3.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -139,25 +142,23 @@
|
||||||
<version>4.3.10.RELEASE</version>
|
<version>4.3.10.RELEASE</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.cache</groupId>
|
||||||
|
<artifactId>cache-api</artifactId>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
<version>20.0</version>
|
<version>20.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Caching -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
|
||||||
<artifactId>caffeine</artifactId>
|
|
||||||
<version>2.5.3</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Metrics -->
|
<!-- Metrics -->
|
||||||
<!-- https://mvnrepository.com/artifact/io.dropwizard.metrics/metrics-core -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.dropwizard.metrics</groupId>
|
<groupId>io.dropwizard.metrics</groupId>
|
||||||
<artifactId>metrics-core</artifactId>
|
<artifactId>metrics-core</artifactId>
|
||||||
<version>3.2.3</version>
|
<version>3.2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Validation -->
|
<!-- Validation -->
|
||||||
|
@ -210,6 +211,24 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.exprofesso</groupId>
|
||||||
|
<artifactId>guava-jcache</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>javax.cache</groupId>
|
||||||
|
<artifactId>cache-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
|
@ -224,13 +243,6 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.stephenc</groupId>
|
|
||||||
<artifactId>jamm</artifactId>
|
|
||||||
<version>0.2.5</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-library</artifactId>
|
<artifactId>hamcrest-library</artifactId>
|
||||||
|
@ -265,7 +277,6 @@
|
||||||
<version>1.7.1</version>
|
<version>1.7.1</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -5,18 +5,19 @@ import java.util.List;
|
||||||
|
|
||||||
public class DefaultMetadata extends Metadata {
|
public class DefaultMetadata extends Metadata {
|
||||||
|
|
||||||
public DefaultMetadata() { super(null); }
|
public DefaultMetadata() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
private DefaultMetadata(Cluster.Manager cluster) {
|
private DefaultMetadata(Cluster.Manager cluster) {
|
||||||
super(cluster);
|
super(cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TupleType newTupleType(DataType... types) {
|
public TupleType newTupleType(DataType... types) {
|
||||||
return newTupleType(Arrays.asList(types));
|
return newTupleType(Arrays.asList(types));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TupleType newTupleType(List<DataType> types) {
|
|
||||||
return new TupleType(types, ProtocolVersion.NEWEST_SUPPORTED, CodecRegistry.DEFAULT_INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public TupleType newTupleType(List<DataType> types) {
|
||||||
|
return new TupleType(types, ProtocolVersion.NEWEST_SUPPORTED, CodecRegistry.DEFAULT_INSTANCE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 com.datastax.driver.core.querybuilder;
|
||||||
|
|
||||||
|
import com.datastax.driver.core.CodecRegistry;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IsNotNullClause extends Clause {
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
public IsNotNullClause(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Object firstValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void appendTo(StringBuilder sb, List<Object> variables, CodecRegistry codecRegistry) {
|
||||||
|
Utils.appendName(name, sb).append(" IS NOT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean containsBindMarker() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,132 +1,155 @@
|
||||||
package com.datastax.driver.core.schemabuilder;
|
package com.datastax.driver.core.schemabuilder;
|
||||||
|
|
||||||
|
import static com.datastax.driver.core.schemabuilder.SchemaStatement.*;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
import static com.datastax.driver.core.schemabuilder.SchemaStatement.STATEMENT_START;
|
|
||||||
import static com.datastax.driver.core.schemabuilder.SchemaStatement.validateNotEmpty;
|
|
||||||
import static com.datastax.driver.core.schemabuilder.SchemaStatement.validateNotKeyWord;
|
|
||||||
|
|
||||||
public class CreateCustomIndex extends CreateIndex {
|
public class CreateCustomIndex extends CreateIndex {
|
||||||
|
|
||||||
private String indexName;
|
private String indexName;
|
||||||
private boolean ifNotExists = false;
|
private boolean ifNotExists = false;
|
||||||
private Optional<String> keyspaceName = Optional.absent();
|
private Optional<String> keyspaceName = Optional.absent();
|
||||||
private String tableName;
|
private String tableName;
|
||||||
private String columnName;
|
private String columnName;
|
||||||
private boolean keys;
|
private boolean keys;
|
||||||
|
|
||||||
CreateCustomIndex(String indexName) {
|
CreateCustomIndex(String indexName) {
|
||||||
super(indexName);
|
super(indexName);
|
||||||
validateNotEmpty(indexName, "Index name");
|
validateNotEmpty(indexName, "Index name");
|
||||||
validateNotKeyWord(indexName, String.format("The index name '%s' is not allowed because it is a reserved keyword", indexName));
|
validateNotKeyWord(
|
||||||
this.indexName = indexName;
|
indexName,
|
||||||
|
String.format(
|
||||||
|
"The index name '%s' is not allowed because it is a reserved keyword", indexName));
|
||||||
|
this.indexName = indexName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the 'IF NOT EXISTS' condition to this CREATE INDEX statement.
|
||||||
|
*
|
||||||
|
* @return this CREATE INDEX statement.
|
||||||
|
*/
|
||||||
|
public CreateIndex ifNotExists() {
|
||||||
|
this.ifNotExists = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the keyspace and table to create the index on.
|
||||||
|
*
|
||||||
|
* @param keyspaceName the keyspace name.
|
||||||
|
* @param tableName the table name.
|
||||||
|
* @return a {@link CreateIndex.CreateIndexOn} that will allow the specification of the column.
|
||||||
|
*/
|
||||||
|
public CreateIndex.CreateIndexOn onTable(String keyspaceName, String tableName) {
|
||||||
|
validateNotEmpty(keyspaceName, "Keyspace name");
|
||||||
|
validateNotEmpty(tableName, "Table name");
|
||||||
|
validateNotKeyWord(
|
||||||
|
keyspaceName,
|
||||||
|
String.format(
|
||||||
|
"The keyspace name '%s' is not allowed because it is a reserved keyword",
|
||||||
|
keyspaceName));
|
||||||
|
validateNotKeyWord(
|
||||||
|
tableName,
|
||||||
|
String.format(
|
||||||
|
"The table name '%s' is not allowed because it is a reserved keyword", tableName));
|
||||||
|
this.keyspaceName = Optional.fromNullable(keyspaceName);
|
||||||
|
this.tableName = tableName;
|
||||||
|
return new CreateCustomIndex.CreateIndexOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the table to create the index on.
|
||||||
|
*
|
||||||
|
* @param tableName the table name.
|
||||||
|
* @return a {@link CreateIndex.CreateIndexOn} that will allow the specification of the column.
|
||||||
|
*/
|
||||||
|
public CreateIndex.CreateIndexOn onTable(String tableName) {
|
||||||
|
validateNotEmpty(tableName, "Table name");
|
||||||
|
validateNotKeyWord(
|
||||||
|
tableName,
|
||||||
|
String.format(
|
||||||
|
"The table name '%s' is not allowed because it is a reserved keyword", tableName));
|
||||||
|
this.tableName = tableName;
|
||||||
|
return new CreateCustomIndex.CreateIndexOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getCustomClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String getOptions() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String buildInternal() {
|
||||||
|
StringBuilder createStatement =
|
||||||
|
new StringBuilder(STATEMENT_START).append("CREATE CUSTOM INDEX ");
|
||||||
|
|
||||||
|
if (ifNotExists) {
|
||||||
|
createStatement.append("IF NOT EXISTS ");
|
||||||
|
}
|
||||||
|
|
||||||
|
createStatement.append(indexName).append(" ON ");
|
||||||
|
|
||||||
|
if (keyspaceName.isPresent()) {
|
||||||
|
createStatement.append(keyspaceName.get()).append(".");
|
||||||
|
}
|
||||||
|
createStatement.append(tableName);
|
||||||
|
|
||||||
|
createStatement.append("(");
|
||||||
|
if (keys) {
|
||||||
|
createStatement.append("KEYS(");
|
||||||
|
}
|
||||||
|
|
||||||
|
createStatement.append(columnName);
|
||||||
|
|
||||||
|
if (keys) {
|
||||||
|
createStatement.append(")");
|
||||||
|
}
|
||||||
|
createStatement.append(")");
|
||||||
|
|
||||||
|
createStatement.append(" USING '");
|
||||||
|
createStatement.append(getCustomClassName());
|
||||||
|
createStatement.append("' WITH OPTIONS = {");
|
||||||
|
createStatement.append(getOptions());
|
||||||
|
createStatement.append(" }");
|
||||||
|
|
||||||
|
return createStatement.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateIndexOn extends CreateIndex.CreateIndexOn {
|
||||||
|
/**
|
||||||
|
* Specify the column to create the index on.
|
||||||
|
*
|
||||||
|
* @param columnName the column name.
|
||||||
|
* @return the final CREATE INDEX statement.
|
||||||
|
*/
|
||||||
|
public SchemaStatement andColumn(String columnName) {
|
||||||
|
validateNotEmpty(columnName, "Column name");
|
||||||
|
validateNotKeyWord(
|
||||||
|
columnName,
|
||||||
|
String.format(
|
||||||
|
"The column name '%s' is not allowed because it is a reserved keyword", columnName));
|
||||||
|
CreateCustomIndex.this.columnName = columnName;
|
||||||
|
return SchemaStatement.fromQueryString(buildInternal());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the 'IF NOT EXISTS' condition to this CREATE INDEX statement.
|
* Create an index on the keys of the given map column.
|
||||||
*
|
*
|
||||||
* @return this CREATE INDEX statement.
|
* @param columnName the column name.
|
||||||
|
* @return the final CREATE INDEX statement.
|
||||||
*/
|
*/
|
||||||
public CreateIndex ifNotExists() {
|
public SchemaStatement andKeysOfColumn(String columnName) {
|
||||||
this.ifNotExists = true;
|
validateNotEmpty(columnName, "Column name");
|
||||||
return this;
|
validateNotKeyWord(
|
||||||
}
|
columnName,
|
||||||
|
String.format(
|
||||||
/**
|
"The column name '%s' is not allowed because it is a reserved keyword", columnName));
|
||||||
* Specify the keyspace and table to create the index on.
|
CreateCustomIndex.this.columnName = columnName;
|
||||||
*
|
CreateCustomIndex.this.keys = true;
|
||||||
* @param keyspaceName the keyspace name.
|
return SchemaStatement.fromQueryString(buildInternal());
|
||||||
* @param tableName the table name.
|
|
||||||
* @return a {@link CreateIndex.CreateIndexOn} that will allow the specification of the column.
|
|
||||||
*/
|
|
||||||
public CreateIndex.CreateIndexOn onTable(String keyspaceName, String tableName) {
|
|
||||||
validateNotEmpty(keyspaceName, "Keyspace name");
|
|
||||||
validateNotEmpty(tableName, "Table name");
|
|
||||||
validateNotKeyWord(keyspaceName, String.format("The keyspace name '%s' is not allowed because it is a reserved keyword", keyspaceName));
|
|
||||||
validateNotKeyWord(tableName, String.format("The table name '%s' is not allowed because it is a reserved keyword", tableName));
|
|
||||||
this.keyspaceName = Optional.fromNullable(keyspaceName);
|
|
||||||
this.tableName = tableName;
|
|
||||||
return new CreateCustomIndex.CreateIndexOn();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the table to create the index on.
|
|
||||||
*
|
|
||||||
* @param tableName the table name.
|
|
||||||
* @return a {@link CreateIndex.CreateIndexOn} that will allow the specification of the column.
|
|
||||||
*/
|
|
||||||
public CreateIndex.CreateIndexOn onTable(String tableName) {
|
|
||||||
validateNotEmpty(tableName, "Table name");
|
|
||||||
validateNotKeyWord(tableName, String.format("The table name '%s' is not allowed because it is a reserved keyword", tableName));
|
|
||||||
this.tableName = tableName;
|
|
||||||
return new CreateCustomIndex.CreateIndexOn();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateIndexOn extends CreateIndex.CreateIndexOn {
|
|
||||||
/**
|
|
||||||
* Specify the column to create the index on.
|
|
||||||
*
|
|
||||||
* @param columnName the column name.
|
|
||||||
* @return the final CREATE INDEX statement.
|
|
||||||
*/
|
|
||||||
public SchemaStatement andColumn(String columnName) {
|
|
||||||
validateNotEmpty(columnName, "Column name");
|
|
||||||
validateNotKeyWord(columnName, String.format("The column name '%s' is not allowed because it is a reserved keyword", columnName));
|
|
||||||
CreateCustomIndex.this.columnName = columnName;
|
|
||||||
return SchemaStatement.fromQueryString(buildInternal());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an index on the keys of the given map column.
|
|
||||||
*
|
|
||||||
* @param columnName the column name.
|
|
||||||
* @return the final CREATE INDEX statement.
|
|
||||||
*/
|
|
||||||
public SchemaStatement andKeysOfColumn(String columnName) {
|
|
||||||
validateNotEmpty(columnName, "Column name");
|
|
||||||
validateNotKeyWord(columnName, String.format("The column name '%s' is not allowed because it is a reserved keyword", columnName));
|
|
||||||
CreateCustomIndex.this.columnName = columnName;
|
|
||||||
CreateCustomIndex.this.keys = true;
|
|
||||||
return SchemaStatement.fromQueryString(buildInternal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String getCustomClassName() { return ""; }
|
|
||||||
String getOptions() { return ""; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String buildInternal() {
|
|
||||||
StringBuilder createStatement = new StringBuilder(STATEMENT_START).append("CREATE CUSTOM INDEX ");
|
|
||||||
|
|
||||||
if (ifNotExists) {
|
|
||||||
createStatement.append("IF NOT EXISTS ");
|
|
||||||
}
|
|
||||||
|
|
||||||
createStatement.append(indexName).append(" ON ");
|
|
||||||
|
|
||||||
if (keyspaceName.isPresent()) {
|
|
||||||
createStatement.append(keyspaceName.get()).append(".");
|
|
||||||
}
|
|
||||||
createStatement.append(tableName);
|
|
||||||
|
|
||||||
createStatement.append("(");
|
|
||||||
if (keys) {
|
|
||||||
createStatement.append("KEYS(");
|
|
||||||
}
|
|
||||||
|
|
||||||
createStatement.append(columnName);
|
|
||||||
|
|
||||||
if (keys) {
|
|
||||||
createStatement.append(")");
|
|
||||||
}
|
|
||||||
createStatement.append(")");
|
|
||||||
|
|
||||||
createStatement.append(" USING '");
|
|
||||||
createStatement.append(getCustomClassName());
|
|
||||||
createStatement.append("' WITH OPTIONS = {");
|
|
||||||
createStatement.append(getOptions());
|
|
||||||
createStatement.append(" }");
|
|
||||||
|
|
||||||
return createStatement.toString();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.datastax.driver.core.schemabuilder;
|
||||||
|
|
||||||
|
import com.datastax.driver.core.CodecRegistry;
|
||||||
|
import com.datastax.driver.core.querybuilder.Select;
|
||||||
|
|
||||||
|
public class CreateMaterializedView extends Create {
|
||||||
|
|
||||||
|
private final String viewName;
|
||||||
|
private Select.Where selection;
|
||||||
|
private String primaryKey;
|
||||||
|
private String clustering;
|
||||||
|
|
||||||
|
public CreateMaterializedView(
|
||||||
|
String keyspaceName,
|
||||||
|
String viewName,
|
||||||
|
Select.Where selection,
|
||||||
|
String primaryKey,
|
||||||
|
String clustering) {
|
||||||
|
super(keyspaceName, viewName);
|
||||||
|
this.viewName = viewName;
|
||||||
|
this.selection = selection;
|
||||||
|
this.primaryKey = primaryKey;
|
||||||
|
this.clustering = clustering;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQueryString(CodecRegistry codecRegistry) {
|
||||||
|
return buildInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String buildInternal() {
|
||||||
|
StringBuilder createStatement =
|
||||||
|
new StringBuilder(STATEMENT_START).append("CREATE MATERIALIZED VIEW");
|
||||||
|
if (ifNotExists) {
|
||||||
|
createStatement.append(" IF NOT EXISTS");
|
||||||
|
}
|
||||||
|
createStatement.append(" ");
|
||||||
|
if (keyspaceName.isPresent()) {
|
||||||
|
createStatement.append(keyspaceName.get()).append(".");
|
||||||
|
}
|
||||||
|
createStatement.append(viewName);
|
||||||
|
createStatement.append(" AS ");
|
||||||
|
createStatement.append(selection.getQueryString());
|
||||||
|
createStatement.setLength(createStatement.length() - 1);
|
||||||
|
createStatement.append(" ");
|
||||||
|
createStatement.append(primaryKey);
|
||||||
|
if (clustering != null) {
|
||||||
|
createStatement.append(" ").append(clustering);
|
||||||
|
}
|
||||||
|
createStatement.append(";");
|
||||||
|
|
||||||
|
return createStatement.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return buildInternal();
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,17 +2,17 @@ package com.datastax.driver.core.schemabuilder;
|
||||||
|
|
||||||
public class CreateSasiIndex extends CreateCustomIndex {
|
public class CreateSasiIndex extends CreateCustomIndex {
|
||||||
|
|
||||||
public CreateSasiIndex(String indexName) {
|
public CreateSasiIndex(String indexName) {
|
||||||
super(indexName);
|
super(indexName);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getCustomClassName() {
|
String getCustomClassName() {
|
||||||
return "org.apache.cassandra.index.sasi.SASIIndex";
|
return "org.apache.cassandra.index.sasi.SASIIndex";
|
||||||
}
|
}
|
||||||
|
|
||||||
String getOptions() {
|
String getOptions() {
|
||||||
return "'analyzer_class': "
|
return "'analyzer_class': "
|
||||||
+ "'org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer', "
|
+ "'org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer', "
|
||||||
+ "'case_sensitive': 'false'";
|
+ "'case_sensitive': 'false'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,24 +17,22 @@ package com.datastax.driver.core.schemabuilder;
|
||||||
|
|
||||||
import com.datastax.driver.core.CodecRegistry;
|
import com.datastax.driver.core.CodecRegistry;
|
||||||
|
|
||||||
/**
|
/** A built CREATE TABLE statement. */
|
||||||
* A built CREATE TABLE statement.
|
|
||||||
*/
|
|
||||||
public class CreateTable extends Create {
|
public class CreateTable extends Create {
|
||||||
|
|
||||||
public CreateTable(String keyspaceName, String tableName) {
|
public CreateTable(String keyspaceName, String tableName) {
|
||||||
super(keyspaceName, tableName);
|
super(keyspaceName, tableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateTable(String tableName) {
|
public CreateTable(String tableName) {
|
||||||
super(tableName);
|
super(tableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getQueryString(CodecRegistry codecRegistry) {
|
public String getQueryString(CodecRegistry codecRegistry) {
|
||||||
return buildInternal();
|
return buildInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return buildInternal();
|
return buildInternal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.datastax.driver.core.schemabuilder;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
public class DropMaterializedView extends Drop {
|
||||||
|
|
||||||
|
private Optional<String> keyspaceName = Optional.absent();
|
||||||
|
private String itemName;
|
||||||
|
private boolean ifExists = true;
|
||||||
|
|
||||||
|
public DropMaterializedView(String keyspaceName, String viewName) {
|
||||||
|
this(keyspaceName, viewName, DroppedItem.MATERIALIZED_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DropMaterializedView(String keyspaceName, String viewName, DroppedItem itemType) {
|
||||||
|
super(keyspaceName, viewName, Drop.DroppedItem.TABLE);
|
||||||
|
validateNotEmpty(keyspaceName, "Keyspace name");
|
||||||
|
this.keyspaceName = Optional.fromNullable(keyspaceName);
|
||||||
|
this.itemName = viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the 'IF EXISTS' condition to this DROP statement.
|
||||||
|
*
|
||||||
|
* @return this statement.
|
||||||
|
*/
|
||||||
|
public Drop ifExists() {
|
||||||
|
this.ifExists = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String buildInternal() {
|
||||||
|
StringBuilder dropStatement = new StringBuilder("DROP MATERIALIZED VIEW ");
|
||||||
|
if (ifExists) {
|
||||||
|
dropStatement.append("IF EXISTS ");
|
||||||
|
}
|
||||||
|
if (keyspaceName.isPresent()) {
|
||||||
|
dropStatement.append(keyspaceName.get()).append(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
dropStatement.append(itemName);
|
||||||
|
return dropStatement.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DroppedItem {
|
||||||
|
TABLE,
|
||||||
|
TYPE,
|
||||||
|
INDEX,
|
||||||
|
MATERIALIZED_VIEW
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,49 +1,48 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
*
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* You may obtain a copy of the License at
|
* 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
|
*
|
||||||
*
|
* 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,
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* See the License for the specific language governing permissions and
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* limitations under the License.
|
* See the License for the specific language governing permissions and
|
||||||
*/
|
* limitations under the License.
|
||||||
package net.helenus.config;
|
*/
|
||||||
|
package net.helenus.config;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.function.Function;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Function;
|
||||||
import net.helenus.core.DslInstantiator;
|
import net.helenus.core.DslInstantiator;
|
||||||
import net.helenus.core.MapperInstantiator;
|
import net.helenus.core.MapperInstantiator;
|
||||||
import net.helenus.core.reflect.ReflectionDslInstantiator;
|
import net.helenus.core.reflect.ReflectionDslInstantiator;
|
||||||
import net.helenus.core.reflect.ReflectionMapperInstantiator;
|
import net.helenus.core.reflect.ReflectionMapperInstantiator;
|
||||||
import net.helenus.mapping.convert.CamelCaseToUnderscoreConverter;
|
import net.helenus.mapping.convert.CamelCaseToUnderscoreConverter;
|
||||||
|
|
||||||
public class DefaultHelenusSettings implements HelenusSettings {
|
public class DefaultHelenusSettings implements HelenusSettings {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Function<String, String> getPropertyToColumnConverter() {
|
public Function<String, String> getPropertyToColumnConverter() {
|
||||||
return CamelCaseToUnderscoreConverter.INSTANCE;
|
return CamelCaseToUnderscoreConverter.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Function<Method, Boolean> getGetterMethodDetector() {
|
public Function<Method, Boolean> getGetterMethodDetector() {
|
||||||
return GetterMethodDetector.INSTANCE;
|
return GetterMethodDetector.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DslInstantiator getDslInstantiator() {
|
public DslInstantiator getDslInstantiator() {
|
||||||
return ReflectionDslInstantiator.INSTANCE;
|
return ReflectionDslInstantiator.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MapperInstantiator getMapperInstantiator() {
|
public MapperInstantiator getMapperInstantiator() {
|
||||||
return ReflectionMapperInstantiator.INSTANCE;
|
return ReflectionMapperInstantiator.INSTANCE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,47 +1,49 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
*
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* You may obtain a copy of the License at
|
* 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
|
*
|
||||||
*
|
* 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,
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* See the License for the specific language governing permissions and
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* limitations under the License.
|
* See the License for the specific language governing permissions and
|
||||||
*/
|
* limitations under the License.
|
||||||
package net.helenus.config;
|
*/
|
||||||
|
package net.helenus.config;
|
||||||
import net.helenus.mapping.annotation.Transient;
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import net.helenus.mapping.annotation.Transient;
|
||||||
public enum GetterMethodDetector implements Function<Method, Boolean> {
|
|
||||||
|
public enum GetterMethodDetector implements Function<Method, Boolean> {
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean apply(Method method) {
|
public Boolean apply(Method method) {
|
||||||
|
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new IllegalArgumentException("empty parameter");
|
throw new IllegalArgumentException("empty parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
|
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods marked "Transient" are not mapped, skip them.
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
if (method.getDeclaredAnnotation(Transient.class) != null) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
// Methods marked "Transient" are not mapped, skip them.
|
||||||
return true;
|
if (method.getDeclaredAnnotation(Transient.class) != null) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +1,33 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
*
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* You may obtain a copy of the License at
|
* 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
|
*
|
||||||
*
|
* 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,
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* See the License for the specific language governing permissions and
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* limitations under the License.
|
* See the License for the specific language governing permissions and
|
||||||
*/
|
* limitations under the License.
|
||||||
package net.helenus.config;
|
*/
|
||||||
|
package net.helenus.config;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.function.Function;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Function;
|
||||||
import net.helenus.core.DslInstantiator;
|
import net.helenus.core.DslInstantiator;
|
||||||
import net.helenus.core.MapperInstantiator;
|
import net.helenus.core.MapperInstantiator;
|
||||||
|
|
||||||
public interface HelenusSettings {
|
public interface HelenusSettings {
|
||||||
|
|
||||||
Function<String, String> getPropertyToColumnConverter();
|
Function<String, String> getPropertyToColumnConverter();
|
||||||
|
|
||||||
Function<Method, Boolean> getGetterMethodDetector();
|
Function<Method, Boolean> getGetterMethodDetector();
|
||||||
|
|
||||||
DslInstantiator getDslInstantiator();
|
DslInstantiator getDslInstantiator();
|
||||||
|
|
||||||
MapperInstantiator getMapperInstantiator();
|
MapperInstantiator getMapperInstantiator();
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package net.helenus.core;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Date;
|
||||||
|
import net.helenus.core.reflect.MapExportable;
|
||||||
|
|
||||||
|
public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<E> {
|
||||||
|
|
||||||
|
public AbstractAuditedEntityDraft(MapExportable entity) {
|
||||||
|
super(entity);
|
||||||
|
|
||||||
|
Date in = new Date();
|
||||||
|
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
|
||||||
|
Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
|
||||||
|
String who = getCurrentAuditor();
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
if (who != null) {
|
||||||
|
set("createdBy", who);
|
||||||
|
}
|
||||||
|
set("createdAt", now);
|
||||||
|
}
|
||||||
|
if (who != null) {
|
||||||
|
set("modifiedBy", who);
|
||||||
|
}
|
||||||
|
set("modifiedAt", now);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getCurrentAuditor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date createdAt() {
|
||||||
|
return get("createdAt", Date.class);
|
||||||
|
}
|
||||||
|
}
|
202
src/main/java/net/helenus/core/AbstractEntityDraft.java
Normal file
202
src/main/java/net/helenus/core/AbstractEntityDraft.java
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
package net.helenus.core;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Primitives;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import net.helenus.core.reflect.DefaultPrimitiveTypes;
|
||||||
|
import net.helenus.core.reflect.Drafted;
|
||||||
|
import net.helenus.core.reflect.MapExportable;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
|
|
||||||
|
public abstract class AbstractEntityDraft<E> implements Drafted<E> {
|
||||||
|
|
||||||
|
private final MapExportable entity;
|
||||||
|
private final Map<String, Object> valuesMap;
|
||||||
|
private final Set<String> readSet;
|
||||||
|
private final Map<String, Object> mutationsMap = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
public AbstractEntityDraft(MapExportable entity) {
|
||||||
|
this.entity = entity;
|
||||||
|
// Entities can mutate their map.
|
||||||
|
if (entity != null) {
|
||||||
|
this.valuesMap = entity.toMap(true);
|
||||||
|
this.readSet = entity.toReadSet();
|
||||||
|
} else {
|
||||||
|
this.valuesMap = new HashMap<String, Object>();
|
||||||
|
this.readSet = new HashSet<String>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Class<E> getEntityClass();
|
||||||
|
|
||||||
|
public E build() {
|
||||||
|
return Helenus.map(getEntityClass(), toMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(Getter<T> getter, Class<?> returnType) {
|
||||||
|
return (T) get(this.<T>methodNameFor(getter), returnType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(String key, Class<?> returnType) {
|
||||||
|
readSet.add(key);
|
||||||
|
T value = (T) mutationsMap.get(key);
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
value = (T) valuesMap.get(key);
|
||||||
|
if (value == null) {
|
||||||
|
|
||||||
|
if (Primitives.allPrimitiveTypes().contains(returnType)) {
|
||||||
|
|
||||||
|
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
||||||
|
if (type == null) {
|
||||||
|
throw new RuntimeException("unknown primitive type " + returnType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T) type.getDefaultValue();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Collections fetched from the valuesMap
|
||||||
|
if (value instanceof Collection) {
|
||||||
|
value = (T) SerializationUtils.<Serializable>clone((Serializable) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Object set(Getter<T> getter, Object value) {
|
||||||
|
HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
|
||||||
|
String key = prop.getPropertyName();
|
||||||
|
|
||||||
|
HelenusValidator.INSTANCE.validate(prop, value);
|
||||||
|
|
||||||
|
if (key == null || value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutationsMap.put(key, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object set(String key, Object value) {
|
||||||
|
if (key == null || value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutationsMap.put(key, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, Object value) {
|
||||||
|
mutationsMap.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T mutate(Getter<T> getter, T value) {
|
||||||
|
return (T) mutate(this.<T>methodNameFor(getter), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T mutate(String key, T value) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
if (entity != null) {
|
||||||
|
T currentValue = this.<T>fetch(key);
|
||||||
|
if (!value.equals(currentValue)) {
|
||||||
|
mutationsMap.put(key, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mutationsMap.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> String methodNameFor(Getter<T> getter) {
|
||||||
|
return MappingUtil.resolveMappingProperty(getter).getProperty().getPropertyName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Object unset(Getter<T> getter) {
|
||||||
|
return unset(methodNameFor(getter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unset(String key) {
|
||||||
|
if (key != null) {
|
||||||
|
Object value = mutationsMap.get(key);
|
||||||
|
mutationsMap.put(key, null);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> boolean reset(Getter<T> getter, T desiredValue) {
|
||||||
|
return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T fetch(String key) {
|
||||||
|
T value = (T) mutationsMap.get(key);
|
||||||
|
if (value == null) {
|
||||||
|
value = (T) valuesMap.get(key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> boolean reset(String key, T desiredValue) {
|
||||||
|
if (key != null && desiredValue != null) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T currentValue = (T) this.<T>fetch(key);
|
||||||
|
if (currentValue == null || !currentValue.equals(desiredValue)) {
|
||||||
|
set(key, desiredValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
return toMap(valuesMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap(Map<String, Object> entityMap) {
|
||||||
|
Map<String, Object> combined;
|
||||||
|
if (entityMap != null && entityMap.size() > 0) {
|
||||||
|
combined = new HashMap<String, Object>(entityMap.size());
|
||||||
|
for (Map.Entry<String, Object> e : entityMap.entrySet()) {
|
||||||
|
combined.put(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
combined = new HashMap<String, Object>(mutationsMap.size());
|
||||||
|
}
|
||||||
|
for (String key : mutated()) {
|
||||||
|
combined.put(key, mutationsMap.get(key));
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> mutated() {
|
||||||
|
return mutationsMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> read() {
|
||||||
|
return readSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mutationsMap.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,133 +16,118 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
import com.datastax.driver.core.*;
|
||||||
|
import com.google.common.base.Stopwatch;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
import com.datastax.driver.core.schemabuilder.SchemaStatement;
|
import net.helenus.mapping.value.ColumnValuePreparer;
|
||||||
|
import net.helenus.mapping.value.ColumnValueProvider;
|
||||||
|
import net.helenus.support.Either;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.datastax.driver.core.*;
|
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import net.helenus.mapping.value.ColumnValuePreparer;
|
|
||||||
import net.helenus.mapping.value.ColumnValueProvider;
|
|
||||||
import net.helenus.support.HelenusException;
|
|
||||||
|
|
||||||
import javax.xml.validation.Schema;
|
|
||||||
|
|
||||||
public abstract class AbstractSessionOperations {
|
public abstract class AbstractSessionOperations {
|
||||||
|
|
||||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
private static final Logger LOG = LoggerFactory.getLogger(AbstractSessionOperations.class);
|
||||||
|
|
||||||
abstract public Session currentSession();
|
public abstract Session currentSession();
|
||||||
|
|
||||||
abstract public String usingKeyspace();
|
public abstract String usingKeyspace();
|
||||||
|
|
||||||
abstract public boolean isShowCql();
|
public abstract boolean isShowCql();
|
||||||
|
|
||||||
abstract public PrintStream getPrintStream();
|
public abstract boolean showValues();
|
||||||
|
|
||||||
abstract public Executor getExecutor();
|
public abstract PrintStream getPrintStream();
|
||||||
|
|
||||||
abstract public SessionRepository getSessionRepository();
|
public abstract Executor getExecutor();
|
||||||
|
|
||||||
abstract public ColumnValueProvider getValueProvider();
|
public abstract SessionRepository getSessionRepository();
|
||||||
|
|
||||||
abstract public ColumnValuePreparer getValuePreparer();
|
public abstract ColumnValueProvider getValueProvider();
|
||||||
|
|
||||||
public PreparedStatement prepare(RegularStatement statement) {
|
public abstract ColumnValuePreparer getValuePreparer();
|
||||||
|
|
||||||
try {
|
public abstract ConsistencyLevel getDefaultConsistencyLevel();
|
||||||
|
|
||||||
log(statement, false);
|
public abstract boolean getDefaultQueryIdempotency();
|
||||||
|
|
||||||
return currentSession().prepare(statement);
|
public PreparedStatement prepare(RegularStatement statement) {
|
||||||
|
try {
|
||||||
|
return currentSession().prepare(statement);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw translateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (RuntimeException e) {
|
public ListenableFuture<PreparedStatement> prepareAsync(RegularStatement statement) {
|
||||||
throw translateException(e);
|
try {
|
||||||
}
|
return currentSession().prepareAsync(statement);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw translateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
public ResultSet execute(Statement statement) {
|
||||||
|
return execute(statement, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<PreparedStatement> prepareAsync(RegularStatement statement) {
|
public ResultSet execute(Statement statement, Stopwatch timer) {
|
||||||
|
return execute(statement, null, timer);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
public ResultSet execute(Statement statement, UnitOfWork uow) {
|
||||||
|
return execute(statement, uow, null);
|
||||||
|
}
|
||||||
|
|
||||||
log(statement, false);
|
public ResultSet execute(Statement statement, UnitOfWork uow, Stopwatch timer) {
|
||||||
|
return executeAsync(statement, uow, timer).getUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
return currentSession().prepareAsync(statement);
|
public ResultSetFuture executeAsync(Statement statement) {
|
||||||
|
return executeAsync(statement, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (RuntimeException e) {
|
public ResultSetFuture executeAsync(Statement statement, Stopwatch timer) {
|
||||||
throw translateException(e);
|
return executeAsync(statement, null, timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow) {
|
||||||
|
return executeAsync(statement, uow, null);
|
||||||
|
}
|
||||||
|
|
||||||
public ResultSet execute(Statement statement, boolean showValues) {
|
public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow, Stopwatch timer) {
|
||||||
|
try {
|
||||||
|
return currentSession().executeAsync(statement);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw translateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return executeAsync(statement, showValues).getUninterruptibly();
|
public MetricRegistry getMetricRegistry() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
public void mergeCache(Table<String, String, Either<Object, List<Facet>>> uowCache) {}
|
||||||
|
|
||||||
public ResultSetFuture executeAsync(Statement statement, boolean showValues) {
|
RuntimeException translateException(RuntimeException e) {
|
||||||
|
if (e instanceof HelenusException) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
throw new HelenusException(e);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
public Object checkCache(String tableName, List<Facet> facets) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
log(statement, showValues);
|
public void updateCache(Object pojo, List<Facet> facets) {}
|
||||||
|
|
||||||
return currentSession().executeAsync(statement);
|
|
||||||
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw translateException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(Statement statement, boolean showValues) {
|
|
||||||
|
|
||||||
if (logger.isInfoEnabled()) {
|
|
||||||
logger.info("Execute statement " + statement);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isShowCql()) {
|
|
||||||
|
|
||||||
if (statement instanceof BuiltStatement) {
|
|
||||||
|
|
||||||
BuiltStatement builtStatement = (BuiltStatement) statement;
|
|
||||||
|
|
||||||
if (showValues) {
|
|
||||||
RegularStatement regularStatement = builtStatement.setForceNoValues(true);
|
|
||||||
printCql(regularStatement.getQueryString());
|
|
||||||
} else {
|
|
||||||
printCql(builtStatement.getQueryString());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (statement instanceof RegularStatement) {
|
|
||||||
RegularStatement regularStatement = (RegularStatement) statement;
|
|
||||||
printCql(regularStatement.getQueryString());
|
|
||||||
} else {
|
|
||||||
printCql(statement.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void cache(String key, Object value);
|
|
||||||
|
|
||||||
RuntimeException translateException(RuntimeException e) {
|
|
||||||
|
|
||||||
if (e instanceof HelenusException) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HelenusException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printCql(String cql) {
|
|
||||||
getPrintStream().println(cql);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public void cacheEvict(List<Facet> facets) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,5 +17,8 @@
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
public enum AutoDdl {
|
public enum AutoDdl {
|
||||||
VALIDATE, UPDATE, CREATE, CREATE_DROP;
|
VALIDATE,
|
||||||
|
UPDATE,
|
||||||
|
CREATE,
|
||||||
|
CREATE_DROP;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
public class ConflictingUnitOfWorkException extends Exception {
|
public class ConflictingUnitOfWorkException extends Exception {
|
||||||
|
|
||||||
final UnitOfWork uow;
|
final UnitOfWork uow;
|
||||||
|
|
||||||
ConflictingUnitOfWorkException(UnitOfWork uow) {
|
|
||||||
this.uow = uow;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ConflictingUnitOfWorkException(UnitOfWork uow) {
|
||||||
|
this.uow = uow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,13 +16,15 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.Metadata;
|
import com.datastax.driver.core.Metadata;
|
||||||
|
import java.util.Optional;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
|
|
||||||
public interface DslInstantiator {
|
public interface DslInstantiator {
|
||||||
|
|
||||||
<E> E instantiate(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, Metadata metadata);
|
<E> E instantiate(
|
||||||
|
Class<E> iface,
|
||||||
|
ClassLoader classLoader,
|
||||||
|
Optional<HelenusPropertyNode> parent,
|
||||||
|
Metadata metadata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,99 +16,108 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.querybuilder.Clause;
|
import com.datastax.driver.core.querybuilder.Clause;
|
||||||
|
import java.util.Objects;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.MappingUtil;
|
import net.helenus.mapping.MappingUtil;
|
||||||
import net.helenus.mapping.value.ColumnValuePreparer;
|
import net.helenus.mapping.value.ColumnValuePreparer;
|
||||||
|
|
||||||
public final class Filter<V> {
|
public final class Filter<V> {
|
||||||
|
|
||||||
private final HelenusPropertyNode node;
|
private final HelenusPropertyNode node;
|
||||||
private final Postulate<V> postulate;
|
private final Postulate<V> postulate;
|
||||||
|
|
||||||
private Filter(HelenusPropertyNode node, Postulate<V> postulate) {
|
private Filter(HelenusPropertyNode node, Postulate<V> postulate) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.postulate = postulate;
|
this.postulate = postulate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusPropertyNode getNode() {
|
public static <V> Filter<V> equal(Getter<V> getter, V val) {
|
||||||
return node;
|
return create(getter, Operator.EQ, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Clause getClause(ColumnValuePreparer valuePreparer) {
|
public static <V> Filter<V> in(Getter<V> getter, V... vals) {
|
||||||
return postulate.getClause(node, valuePreparer);
|
Objects.requireNonNull(getter, "empty getter");
|
||||||
}
|
Objects.requireNonNull(vals, "empty values");
|
||||||
|
|
||||||
public static <V> Filter<V> equal(Getter<V> getter, V val) {
|
if (vals.length == 0) {
|
||||||
return create(getter, Operator.EQ, val);
|
throw new IllegalArgumentException("values array is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Filter<V> in(Getter<V> getter, V... vals) {
|
for (int i = 0; i != vals.length; ++i) {
|
||||||
Objects.requireNonNull(getter, "empty getter");
|
Objects.requireNonNull(vals[i], "value[" + i + "] is empty");
|
||||||
Objects.requireNonNull(vals, "empty values");
|
}
|
||||||
|
|
||||||
if (vals.length == 0) {
|
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
||||||
throw new IllegalArgumentException("values array is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i != vals.length; ++i) {
|
Postulate<V> postulate = Postulate.of(Operator.IN, vals);
|
||||||
Objects.requireNonNull(vals[i], "value[" + i + "] is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
return new Filter<V>(node, postulate);
|
||||||
|
}
|
||||||
|
|
||||||
Postulate<V> postulate = Postulate.of(Operator.IN, vals);
|
public static <V> Filter<V> greaterThan(Getter<V> getter, V val) {
|
||||||
|
return create(getter, Operator.GT, val);
|
||||||
|
}
|
||||||
|
|
||||||
return new Filter<V>(node, postulate);
|
public static <V> Filter<V> lessThan(Getter<V> getter, V val) {
|
||||||
}
|
return create(getter, Operator.LT, val);
|
||||||
|
}
|
||||||
|
|
||||||
public static <V> Filter<V> greaterThan(Getter<V> getter, V val) {
|
public static <V> Filter<V> greaterThanOrEqual(Getter<V> getter, V val) {
|
||||||
return create(getter, Operator.GT, val);
|
return create(getter, Operator.GTE, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Filter<V> lessThan(Getter<V> getter, V val) {
|
public static <V> Filter<V> lessThanOrEqual(Getter<V> getter, V val) {
|
||||||
return create(getter, Operator.LT, val);
|
return create(getter, Operator.LTE, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Filter<V> greaterThanOrEqual(Getter<V> getter, V val) {
|
public static <V> Filter<V> create(Getter<V> getter, Postulate<V> postulate) {
|
||||||
return create(getter, Operator.GTE, val);
|
Objects.requireNonNull(getter, "empty getter");
|
||||||
}
|
Objects.requireNonNull(postulate, "empty operator");
|
||||||
|
|
||||||
public static <V> Filter<V> lessThanOrEqual(Getter<V> getter, V val) {
|
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
||||||
return create(getter, Operator.LTE, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> Filter<V> create(Getter<V> getter, Postulate<V> postulate) {
|
return new Filter<V>(node, postulate);
|
||||||
Objects.requireNonNull(getter, "empty getter");
|
}
|
||||||
Objects.requireNonNull(postulate, "empty operator");
|
|
||||||
|
|
||||||
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
public static <V> Filter<V> create(
|
||||||
|
Getter<V> getter, HelenusPropertyNode node, Postulate<V> postulate) {
|
||||||
|
Objects.requireNonNull(getter, "empty getter");
|
||||||
|
Objects.requireNonNull(postulate, "empty operator");
|
||||||
|
return new Filter<V>(node, postulate);
|
||||||
|
}
|
||||||
|
|
||||||
return new Filter<V>(node, postulate);
|
public static <V> Filter<V> create(Getter<V> getter, Operator op, V val) {
|
||||||
}
|
Objects.requireNonNull(getter, "empty getter");
|
||||||
|
Objects.requireNonNull(op, "empty op");
|
||||||
|
Objects.requireNonNull(val, "empty value");
|
||||||
|
|
||||||
public static <V> Filter<V> create(Getter<V> getter, Operator op, V val) {
|
if (op == Operator.IN) {
|
||||||
Objects.requireNonNull(getter, "empty getter");
|
throw new IllegalArgumentException(
|
||||||
Objects.requireNonNull(op, "empty op");
|
"invalid usage of the 'in' operator, use Filter.in() static method");
|
||||||
Objects.requireNonNull(val, "empty value");
|
}
|
||||||
|
|
||||||
if (op == Operator.IN) {
|
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
||||||
throw new IllegalArgumentException("invalid usage of the 'in' operator, use Filter.in() static method");
|
|
||||||
}
|
|
||||||
|
|
||||||
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
Postulate<V> postulate = Postulate.of(op, val);
|
||||||
|
|
||||||
Postulate<V> postulate = Postulate.of(op, val);
|
return new Filter<V>(node, postulate);
|
||||||
|
}
|
||||||
|
|
||||||
return new Filter<V>(node, postulate);
|
public HelenusPropertyNode getNode() {
|
||||||
}
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public Clause getClause(ColumnValuePreparer valuePreparer) {
|
||||||
public String toString() {
|
return postulate.getClause(node, valuePreparer);
|
||||||
return node.getColumnName() + postulate.toString();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public V[] postulateValues() {
|
||||||
|
return postulate.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return node.getColumnName() + postulate.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,6 +18,5 @@ package net.helenus.core;
|
||||||
|
|
||||||
public interface Getter<V> {
|
public interface Getter<V> {
|
||||||
|
|
||||||
V get();
|
V get();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,14 +16,15 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.Cluster;
|
import com.datastax.driver.core.Cluster;
|
||||||
import com.datastax.driver.core.Metadata;
|
import com.datastax.driver.core.Metadata;
|
||||||
import com.datastax.driver.core.Session;
|
import com.datastax.driver.core.Session;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import net.helenus.config.DefaultHelenusSettings;
|
import net.helenus.config.DefaultHelenusSettings;
|
||||||
import net.helenus.config.HelenusSettings;
|
import net.helenus.config.HelenusSettings;
|
||||||
import net.helenus.core.reflect.DslExportable;
|
import net.helenus.core.reflect.DslExportable;
|
||||||
|
@ -32,156 +34,170 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class Helenus {
|
public final class Helenus {
|
||||||
|
|
||||||
private static volatile HelenusSettings settings = new DefaultHelenusSettings();
|
private static final ConcurrentMap<Class<?>, Object> dslCache =
|
||||||
private static final ConcurrentMap<Class<?>, Object> dslCache = new ConcurrentHashMap<Class<?>, Object>();
|
new ConcurrentHashMap<Class<?>, Object>();
|
||||||
private static final ConcurrentMap<Class<?>, Metadata> metadataForEntity = new ConcurrentHashMap<Class<?>, Metadata>();
|
private static final ConcurrentMap<Class<?>, Metadata> metadataForEntity =
|
||||||
private static final Set<HelenusSession> sessions = new HashSet<HelenusSession>();
|
new ConcurrentHashMap<Class<?>, Metadata>();
|
||||||
private static volatile HelenusSession singleton;
|
private static final Set<HelenusSession> sessions = new HashSet<HelenusSession>();
|
||||||
|
private static volatile HelenusSettings settings = new DefaultHelenusSettings();
|
||||||
|
private static volatile HelenusSession singleton;
|
||||||
|
|
||||||
|
private Helenus() {}
|
||||||
|
|
||||||
private Helenus() {
|
protected static void setSession(HelenusSession session) {
|
||||||
}
|
sessions.add(session);
|
||||||
|
singleton = session;
|
||||||
|
}
|
||||||
|
|
||||||
protected static void setSession(HelenusSession session) {
|
public static HelenusSession session() {
|
||||||
sessions.add(session);
|
return singleton;
|
||||||
singleton = session;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static HelenusSession session() {
|
public static void shutdown() {
|
||||||
return singleton;
|
sessions.forEach(
|
||||||
}
|
(session) -> {
|
||||||
|
session.close();
|
||||||
public static void shutdown() {
|
sessions.remove(session);
|
||||||
sessions.forEach((session) -> {
|
|
||||||
session.close();
|
|
||||||
sessions.remove(session);
|
|
||||||
});
|
});
|
||||||
dslCache.clear();
|
dslCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HelenusSettings settings() {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HelenusSettings settings(HelenusSettings overrideSettings) {
|
||||||
|
HelenusSettings old = settings;
|
||||||
|
settings = overrideSettings;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionInitializer connect(Cluster cluster) {
|
||||||
|
Session session = cluster.connect();
|
||||||
|
return new SessionInitializer(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionInitializer connect(Cluster cluster, String keyspace) {
|
||||||
|
Session session = cluster.connect(keyspace);
|
||||||
|
return new SessionInitializer(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionInitializer init(Session session, String keyspace) {
|
||||||
|
return new SessionInitializer(session, keyspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionInitializer init(Session session) {
|
||||||
|
|
||||||
|
if (session == null) {
|
||||||
|
throw new IllegalArgumentException("empty session");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HelenusSettings settings() {
|
return new SessionInitializer(session);
|
||||||
return settings;
|
}
|
||||||
|
|
||||||
|
public static void clearDslCache() {
|
||||||
|
dslCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> E dsl(Class<E> iface) {
|
||||||
|
return dsl(iface, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> E dsl(Class<E> iface, Metadata metadata) {
|
||||||
|
return dsl(iface, iface.getClassLoader(), Optional.empty(), metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> E dsl(Class<E> iface, ClassLoader classLoader, Metadata metadata) {
|
||||||
|
return dsl(iface, classLoader, Optional.empty(), metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> E dsl(
|
||||||
|
Class<E> iface,
|
||||||
|
ClassLoader classLoader,
|
||||||
|
Optional<HelenusPropertyNode> parent,
|
||||||
|
Metadata metadata) {
|
||||||
|
|
||||||
|
Object instance = null;
|
||||||
|
|
||||||
|
if (!parent.isPresent()) {
|
||||||
|
instance = dslCache.get(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HelenusSettings settings(HelenusSettings overrideSettings) {
|
if (instance == null) {
|
||||||
HelenusSettings old = settings;
|
|
||||||
settings = overrideSettings;
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SessionInitializer connect(Cluster cluster) {
|
instance = settings.getDslInstantiator().instantiate(iface, classLoader, parent, metadata);
|
||||||
Session session = cluster.connect();
|
|
||||||
return new SessionInitializer(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SessionInitializer connect(Cluster cluster, String keyspace) {
|
if (!parent.isPresent()) {
|
||||||
Session session = cluster.connect(keyspace);
|
|
||||||
return new SessionInitializer(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SessionInitializer init(Session session) {
|
Object c = dslCache.putIfAbsent(iface, instance);
|
||||||
|
if (c != null) {
|
||||||
if (session == null) {
|
instance = c;
|
||||||
throw new IllegalArgumentException("empty session");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return new SessionInitializer(session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearDslCache() {
|
return (E) instance;
|
||||||
dslCache.clear();
|
}
|
||||||
|
|
||||||
|
public static <E> E map(Class<E> iface, Map<String, Object> src) {
|
||||||
|
return map(iface, src, iface.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E> E map(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
|
||||||
|
return settings.getMapperInstantiator().instantiate(iface, src, classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HelenusEntity entity(Class<?> iface) {
|
||||||
|
Metadata metadata = metadataForEntity.get(iface);
|
||||||
|
if (metadata == null) {
|
||||||
|
HelenusSession session = session();
|
||||||
|
if (session != null) {
|
||||||
|
metadata = session.getMetadata();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entity(iface, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HelenusEntity entity(Class<?> iface, Metadata metadata) {
|
||||||
|
|
||||||
|
Object dsl = dsl(iface, metadata);
|
||||||
|
|
||||||
|
DslExportable e = (DslExportable) dsl;
|
||||||
|
|
||||||
|
return e.getHelenusMappingEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HelenusEntity resolve(Object ifaceOrDsl) {
|
||||||
|
return resolve(ifaceOrDsl, metadataForEntity.get(ifaceOrDsl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HelenusEntity resolve(Object ifaceOrDsl, Metadata metadata) {
|
||||||
|
|
||||||
|
if (ifaceOrDsl == null) {
|
||||||
|
throw new HelenusMappingException("ifaceOrDsl is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E> E dsl(Class<E> iface) {
|
if (ifaceOrDsl instanceof DslExportable) {
|
||||||
return dsl(iface, null);
|
|
||||||
|
DslExportable e = (DslExportable) ifaceOrDsl;
|
||||||
|
|
||||||
|
return e.getHelenusMappingEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E> E dsl(Class<E> iface, Metadata metadata) {
|
if (ifaceOrDsl instanceof Class) {
|
||||||
return dsl(iface, iface.getClassLoader(), Optional.empty(), metadata);
|
|
||||||
|
Class<?> iface = (Class<?>) ifaceOrDsl;
|
||||||
|
|
||||||
|
if (!iface.isInterface()) {
|
||||||
|
throw new HelenusMappingException("class is not an interface " + iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata != null) {
|
||||||
|
metadataForEntity.putIfAbsent(iface, metadata);
|
||||||
|
}
|
||||||
|
return entity(iface, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E> E dsl(Class<E> iface, ClassLoader classLoader, Metadata metadata) {
|
throw new HelenusMappingException("unknown dsl object or mapping interface " + ifaceOrDsl);
|
||||||
return dsl(iface, classLoader, Optional.empty(), metadata);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static <E> E dsl(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent,
|
|
||||||
Metadata metadata) {
|
|
||||||
|
|
||||||
Object instance = null;
|
|
||||||
|
|
||||||
if (!parent.isPresent()) {
|
|
||||||
instance = dslCache.get(iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance == null) {
|
|
||||||
|
|
||||||
instance = settings.getDslInstantiator().instantiate(iface, classLoader, parent, metadata);
|
|
||||||
|
|
||||||
if (!parent.isPresent()) {
|
|
||||||
|
|
||||||
Object c = dslCache.putIfAbsent(iface, instance);
|
|
||||||
if (c != null) {
|
|
||||||
instance = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (E) instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <E> E map(Class<E> iface, Map<String, Object> src) {
|
|
||||||
return map(iface, src, iface.getClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <E> E map(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
|
|
||||||
return settings.getMapperInstantiator().instantiate(iface, src, classLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HelenusEntity entity(Class<?> iface) {
|
|
||||||
return entity(iface, metadataForEntity.get(iface));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HelenusEntity entity(Class<?> iface, Metadata metadata) {
|
|
||||||
|
|
||||||
Object dsl = dsl(iface, metadata);
|
|
||||||
|
|
||||||
DslExportable e = (DslExportable) dsl;
|
|
||||||
|
|
||||||
return e.getHelenusMappingEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HelenusEntity resolve(Object ifaceOrDsl) {
|
|
||||||
return resolve(ifaceOrDsl, metadataForEntity.get(ifaceOrDsl));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HelenusEntity resolve(Object ifaceOrDsl, Metadata metadata) {
|
|
||||||
|
|
||||||
if (ifaceOrDsl == null) {
|
|
||||||
throw new HelenusMappingException("ifaceOrDsl is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifaceOrDsl instanceof DslExportable) {
|
|
||||||
|
|
||||||
DslExportable e = (DslExportable) ifaceOrDsl;
|
|
||||||
|
|
||||||
return e.getHelenusMappingEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifaceOrDsl instanceof Class) {
|
|
||||||
|
|
||||||
Class<?> iface = (Class<?>) ifaceOrDsl;
|
|
||||||
|
|
||||||
if (!iface.isInterface()) {
|
|
||||||
throw new HelenusMappingException("class is not an interface " + iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
metadataForEntity.putIfAbsent(iface, metadata);
|
|
||||||
return entity(iface, metadata);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HelenusMappingException("unknown dsl object or mapping interface " + ifaceOrDsl);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,36 +17,32 @@
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
import javax.validation.ConstraintValidator;
|
import javax.validation.ConstraintValidator;
|
||||||
|
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public enum HelenusValidator implements PropertyValueValidator {
|
public enum HelenusValidator implements PropertyValueValidator {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
INSTANCE;
|
public void validate(HelenusProperty prop, Object value) {
|
||||||
|
|
||||||
public void validate(HelenusProperty prop, Object value) {
|
for (ConstraintValidator<? extends Annotation, ?> validator : prop.getValidators()) {
|
||||||
|
|
||||||
for (ConstraintValidator<? extends Annotation, ?> validator : prop.getValidators()) {
|
ConstraintValidator typeless = (ConstraintValidator) validator;
|
||||||
|
|
||||||
ConstraintValidator typeless = (ConstraintValidator) validator;
|
boolean valid = false;
|
||||||
|
|
||||||
boolean valid = false;
|
try {
|
||||||
|
valid = typeless.isValid(value, null);
|
||||||
try {
|
} catch (ClassCastException e) {
|
||||||
valid = typeless.isValid(value, null);
|
throw new HelenusMappingException(
|
||||||
} catch (ClassCastException e) {
|
"validator was used for wrong type '" + value + "' in " + prop, e);
|
||||||
throw new HelenusMappingException("validator was used for wrong type '" + value + "' in " + prop, e);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid) {
|
|
||||||
throw new HelenusException("wrong value '" + value + "' for " + prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
throw new HelenusException("wrong value '" + value + "' for " + prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,6 +20,5 @@ import java.util.Map;
|
||||||
|
|
||||||
public interface MapperInstantiator {
|
public interface MapperInstantiator {
|
||||||
|
|
||||||
<E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader);
|
<E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,10 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.Row;
|
import com.datastax.driver.core.Row;
|
||||||
|
import java.util.function.Function;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
import net.helenus.mapping.value.ColumnValueProvider;
|
import net.helenus.mapping.value.ColumnValueProvider;
|
||||||
|
@ -26,162 +25,203 @@ import net.helenus.support.Fun;
|
||||||
|
|
||||||
public final class Mappers {
|
public final class Mappers {
|
||||||
|
|
||||||
private Mappers() {
|
private Mappers() {}
|
||||||
}
|
|
||||||
|
|
||||||
public final static class Mapper1<A> implements Function<Row, Fun.Tuple1<A>> {
|
public static final class Mapper1<A> implements Function<Row, Fun.Tuple1<A>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1;
|
private final HelenusProperty p1;
|
||||||
|
|
||||||
public Mapper1(ColumnValueProvider provider, HelenusPropertyNode p1) {
|
public Mapper1(ColumnValueProvider provider, HelenusPropertyNode p1) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.p1 = p1.getProperty();
|
this.p1 = p1.getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fun.Tuple1<A> apply(Row row) {
|
public Fun.Tuple1<A> apply(Row row) {
|
||||||
return new Fun.Tuple1<A>(provider.getColumnValue(row, 0, p1));
|
return new Fun.Tuple1<A>(provider.getColumnValue(row, 0, p1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static class Mapper2<A, B> implements Function<Row, Fun.Tuple2<A, B>> {
|
public static final class Mapper2<A, B> implements Function<Row, Fun.Tuple2<A, B>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1;
|
private final HelenusProperty p1;
|
||||||
private final HelenusProperty p2;
|
private final HelenusProperty p2;
|
||||||
|
|
||||||
public Mapper2(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2) {
|
public Mapper2(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.p1 = p1.getProperty();
|
this.p1 = p1.getProperty();
|
||||||
this.p2 = p2.getProperty();
|
this.p2 = p2.getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fun.Tuple2<A, B> apply(Row row) {
|
public Fun.Tuple2<A, B> apply(Row row) {
|
||||||
return new Fun.Tuple2<A, B>(provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2));
|
return new Fun.Tuple2<A, B>(
|
||||||
}
|
provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final static class Mapper3<A, B, C> implements Function<Row, Fun.Tuple3<A, B, C>> {
|
public static final class Mapper3<A, B, C> implements Function<Row, Fun.Tuple3<A, B, C>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1;
|
private final HelenusProperty p1;
|
||||||
private final HelenusProperty p2;
|
private final HelenusProperty p2;
|
||||||
private final HelenusProperty p3;
|
private final HelenusProperty p3;
|
||||||
|
|
||||||
public Mapper3(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2,
|
public Mapper3(
|
||||||
HelenusPropertyNode p3) {
|
ColumnValueProvider provider,
|
||||||
this.provider = provider;
|
HelenusPropertyNode p1,
|
||||||
this.p1 = p1.getProperty();
|
HelenusPropertyNode p2,
|
||||||
this.p2 = p2.getProperty();
|
HelenusPropertyNode p3) {
|
||||||
this.p3 = p3.getProperty();
|
this.provider = provider;
|
||||||
}
|
this.p1 = p1.getProperty();
|
||||||
|
this.p2 = p2.getProperty();
|
||||||
|
this.p3 = p3.getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fun.Tuple3<A, B, C> apply(Row row) {
|
public Fun.Tuple3<A, B, C> apply(Row row) {
|
||||||
return new Fun.Tuple3<A, B, C>(provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2),
|
return new Fun.Tuple3<A, B, C>(
|
||||||
provider.getColumnValue(row, 2, p3));
|
provider.getColumnValue(row, 0, p1),
|
||||||
}
|
provider.getColumnValue(row, 1, p2),
|
||||||
}
|
provider.getColumnValue(row, 2, p3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final static class Mapper4<A, B, C, D> implements Function<Row, Fun.Tuple4<A, B, C, D>> {
|
public static final class Mapper4<A, B, C, D> implements Function<Row, Fun.Tuple4<A, B, C, D>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1;
|
private final HelenusProperty p1;
|
||||||
private final HelenusProperty p2;
|
private final HelenusProperty p2;
|
||||||
private final HelenusProperty p3;
|
private final HelenusProperty p3;
|
||||||
private final HelenusProperty p4;
|
private final HelenusProperty p4;
|
||||||
|
|
||||||
public Mapper4(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2,
|
public Mapper4(
|
||||||
HelenusPropertyNode p3, HelenusPropertyNode p4) {
|
ColumnValueProvider provider,
|
||||||
this.provider = provider;
|
HelenusPropertyNode p1,
|
||||||
this.p1 = p1.getProperty();
|
HelenusPropertyNode p2,
|
||||||
this.p2 = p2.getProperty();
|
HelenusPropertyNode p3,
|
||||||
this.p3 = p3.getProperty();
|
HelenusPropertyNode p4) {
|
||||||
this.p4 = p4.getProperty();
|
this.provider = provider;
|
||||||
}
|
this.p1 = p1.getProperty();
|
||||||
|
this.p2 = p2.getProperty();
|
||||||
|
this.p3 = p3.getProperty();
|
||||||
|
this.p4 = p4.getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fun.Tuple4<A, B, C, D> apply(Row row) {
|
public Fun.Tuple4<A, B, C, D> apply(Row row) {
|
||||||
return new Fun.Tuple4<A, B, C, D>(provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2),
|
return new Fun.Tuple4<A, B, C, D>(
|
||||||
provider.getColumnValue(row, 2, p3), provider.getColumnValue(row, 3, p4));
|
provider.getColumnValue(row, 0, p1),
|
||||||
}
|
provider.getColumnValue(row, 1, p2),
|
||||||
}
|
provider.getColumnValue(row, 2, p3),
|
||||||
|
provider.getColumnValue(row, 3, p4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final static class Mapper5<A, B, C, D, E> implements Function<Row, Fun.Tuple5<A, B, C, D, E>> {
|
public static final class Mapper5<A, B, C, D, E>
|
||||||
|
implements Function<Row, Fun.Tuple5<A, B, C, D, E>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1, p2, p3, p4, p5;
|
private final HelenusProperty p1, p2, p3, p4, p5;
|
||||||
|
|
||||||
public Mapper5(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2,
|
public Mapper5(
|
||||||
HelenusPropertyNode p3, HelenusPropertyNode p4, HelenusPropertyNode p5) {
|
ColumnValueProvider provider,
|
||||||
this.provider = provider;
|
HelenusPropertyNode p1,
|
||||||
this.p1 = p1.getProperty();
|
HelenusPropertyNode p2,
|
||||||
this.p2 = p2.getProperty();
|
HelenusPropertyNode p3,
|
||||||
this.p3 = p3.getProperty();
|
HelenusPropertyNode p4,
|
||||||
this.p4 = p4.getProperty();
|
HelenusPropertyNode p5) {
|
||||||
this.p5 = p5.getProperty();
|
this.provider = provider;
|
||||||
}
|
this.p1 = p1.getProperty();
|
||||||
|
this.p2 = p2.getProperty();
|
||||||
|
this.p3 = p3.getProperty();
|
||||||
|
this.p4 = p4.getProperty();
|
||||||
|
this.p5 = p5.getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fun.Tuple5<A, B, C, D, E> apply(Row row) {
|
public Fun.Tuple5<A, B, C, D, E> apply(Row row) {
|
||||||
return new Fun.Tuple5<A, B, C, D, E>(provider.getColumnValue(row, 0, p1),
|
return new Fun.Tuple5<A, B, C, D, E>(
|
||||||
provider.getColumnValue(row, 1, p2), provider.getColumnValue(row, 2, p3),
|
provider.getColumnValue(row, 0, p1),
|
||||||
provider.getColumnValue(row, 3, p4), provider.getColumnValue(row, 4, p5));
|
provider.getColumnValue(row, 1, p2),
|
||||||
}
|
provider.getColumnValue(row, 2, p3),
|
||||||
}
|
provider.getColumnValue(row, 3, p4),
|
||||||
|
provider.getColumnValue(row, 4, p5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final static class Mapper6<A, B, C, D, E, F> implements Function<Row, Fun.Tuple6<A, B, C, D, E, F>> {
|
public static final class Mapper6<A, B, C, D, E, F>
|
||||||
|
implements Function<Row, Fun.Tuple6<A, B, C, D, E, F>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1, p2, p3, p4, p5, p6;
|
private final HelenusProperty p1, p2, p3, p4, p5, p6;
|
||||||
|
|
||||||
public Mapper6(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2,
|
public Mapper6(
|
||||||
HelenusPropertyNode p3, HelenusPropertyNode p4, HelenusPropertyNode p5, HelenusPropertyNode p6) {
|
ColumnValueProvider provider,
|
||||||
this.provider = provider;
|
HelenusPropertyNode p1,
|
||||||
this.p1 = p1.getProperty();
|
HelenusPropertyNode p2,
|
||||||
this.p2 = p2.getProperty();
|
HelenusPropertyNode p3,
|
||||||
this.p3 = p3.getProperty();
|
HelenusPropertyNode p4,
|
||||||
this.p4 = p4.getProperty();
|
HelenusPropertyNode p5,
|
||||||
this.p5 = p5.getProperty();
|
HelenusPropertyNode p6) {
|
||||||
this.p6 = p6.getProperty();
|
this.provider = provider;
|
||||||
}
|
this.p1 = p1.getProperty();
|
||||||
|
this.p2 = p2.getProperty();
|
||||||
|
this.p3 = p3.getProperty();
|
||||||
|
this.p4 = p4.getProperty();
|
||||||
|
this.p5 = p5.getProperty();
|
||||||
|
this.p6 = p6.getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fun.Tuple6<A, B, C, D, E, F> apply(Row row) {
|
public Fun.Tuple6<A, B, C, D, E, F> apply(Row row) {
|
||||||
return new Fun.Tuple6<A, B, C, D, E, F>(provider.getColumnValue(row, 0, p1),
|
return new Fun.Tuple6<A, B, C, D, E, F>(
|
||||||
provider.getColumnValue(row, 1, p2), provider.getColumnValue(row, 2, p3),
|
provider.getColumnValue(row, 0, p1),
|
||||||
provider.getColumnValue(row, 3, p4), provider.getColumnValue(row, 4, p5),
|
provider.getColumnValue(row, 1, p2),
|
||||||
provider.getColumnValue(row, 5, p6));
|
provider.getColumnValue(row, 2, p3),
|
||||||
}
|
provider.getColumnValue(row, 3, p4),
|
||||||
}
|
provider.getColumnValue(row, 4, p5),
|
||||||
|
provider.getColumnValue(row, 5, p6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final static class Mapper7<A, B, C, D, E, F, G> implements Function<Row, Fun.Tuple7<A, B, C, D, E, F, G>> {
|
public static final class Mapper7<A, B, C, D, E, F, G>
|
||||||
|
implements Function<Row, Fun.Tuple7<A, B, C, D, E, F, G>> {
|
||||||
|
|
||||||
private final ColumnValueProvider provider;
|
private final ColumnValueProvider provider;
|
||||||
private final HelenusProperty p1, p2, p3, p4, p5, p6, p7;
|
private final HelenusProperty p1, p2, p3, p4, p5, p6, p7;
|
||||||
|
|
||||||
public Mapper7(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2,
|
public Mapper7(
|
||||||
HelenusPropertyNode p3, HelenusPropertyNode p4, HelenusPropertyNode p5, HelenusPropertyNode p6,
|
ColumnValueProvider provider,
|
||||||
HelenusPropertyNode p7) {
|
HelenusPropertyNode p1,
|
||||||
this.provider = provider;
|
HelenusPropertyNode p2,
|
||||||
this.p1 = p1.getProperty();
|
HelenusPropertyNode p3,
|
||||||
this.p2 = p2.getProperty();
|
HelenusPropertyNode p4,
|
||||||
this.p3 = p3.getProperty();
|
HelenusPropertyNode p5,
|
||||||
this.p4 = p4.getProperty();
|
HelenusPropertyNode p6,
|
||||||
this.p5 = p5.getProperty();
|
HelenusPropertyNode p7) {
|
||||||
this.p6 = p6.getProperty();
|
this.provider = provider;
|
||||||
this.p7 = p7.getProperty();
|
this.p1 = p1.getProperty();
|
||||||
}
|
this.p2 = p2.getProperty();
|
||||||
|
this.p3 = p3.getProperty();
|
||||||
@Override
|
this.p4 = p4.getProperty();
|
||||||
public Fun.Tuple7<A, B, C, D, E, F, G> apply(Row row) {
|
this.p5 = p5.getProperty();
|
||||||
return new Fun.Tuple7<A, B, C, D, E, F, G>(provider.getColumnValue(row, 0, p1),
|
this.p6 = p6.getProperty();
|
||||||
provider.getColumnValue(row, 1, p2), provider.getColumnValue(row, 2, p3),
|
this.p7 = p7.getProperty();
|
||||||
provider.getColumnValue(row, 3, p4), provider.getColumnValue(row, 4, p5),
|
}
|
||||||
provider.getColumnValue(row, 5, p6), provider.getColumnValue(row, 6, p7));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fun.Tuple7<A, B, C, D, E, F, G> apply(Row row) {
|
||||||
|
return new Fun.Tuple7<A, B, C, D, E, F, G>(
|
||||||
|
provider.getColumnValue(row, 0, p1),
|
||||||
|
provider.getColumnValue(row, 1, p2),
|
||||||
|
provider.getColumnValue(row, 2, p3),
|
||||||
|
provider.getColumnValue(row, 3, p4),
|
||||||
|
provider.getColumnValue(row, 4, p5),
|
||||||
|
provider.getColumnValue(row, 5, p6),
|
||||||
|
provider.getColumnValue(row, 6, p7));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Ben Manes. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* 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 net.helenus.core;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import com.codahale.metrics.Meter;
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
|
||||||
import com.codahale.metrics.Timer;
|
|
||||||
import com.github.benmanes.caffeine.cache.stats.CacheStats;
|
|
||||||
import com.github.benmanes.caffeine.cache.stats.StatsCounter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link StatsCounter} instrumented with Dropwizard Metrics.
|
|
||||||
*
|
|
||||||
* @author ben.manes@gmail.com (Ben Manes)
|
|
||||||
*/
|
|
||||||
public final class MetricsStatsCounter implements StatsCounter {
|
|
||||||
private final Meter hitCount;
|
|
||||||
private final Meter missCount;
|
|
||||||
private final Meter loadSuccessCount;
|
|
||||||
private final Meter loadFailureCount;
|
|
||||||
private final Timer totalLoadTime;
|
|
||||||
private final Meter evictionCount;
|
|
||||||
private final Meter evictionWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance for use by a single cache.
|
|
||||||
*
|
|
||||||
* @param registry the registry of metric instances
|
|
||||||
* @param metricsPrefix the prefix name for the metrics
|
|
||||||
*/
|
|
||||||
public MetricsStatsCounter(MetricRegistry registry, String metricsPrefix) {
|
|
||||||
requireNonNull(metricsPrefix);
|
|
||||||
hitCount = registry.meter(metricsPrefix + ".hits");
|
|
||||||
missCount = registry.meter(metricsPrefix + ".misses");
|
|
||||||
totalLoadTime = registry.timer(metricsPrefix + ".loads");
|
|
||||||
loadSuccessCount = registry.meter(metricsPrefix + ".loads-success");
|
|
||||||
loadFailureCount = registry.meter(metricsPrefix + ".loads-failure");
|
|
||||||
evictionCount = registry.meter(metricsPrefix + ".evictions");
|
|
||||||
evictionWeight = registry.meter(metricsPrefix + ".evictions-weight");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordHits(int count) {
|
|
||||||
hitCount.mark(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordMisses(int count) {
|
|
||||||
missCount.mark(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordLoadSuccess(long loadTime) {
|
|
||||||
loadSuccessCount.mark();
|
|
||||||
totalLoadTime.update(loadTime, TimeUnit.NANOSECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordLoadFailure(long loadTime) {
|
|
||||||
loadFailureCount.mark();
|
|
||||||
totalLoadTime.update(loadTime, TimeUnit.NANOSECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordEviction() {
|
|
||||||
// This method is scheduled for removal in version 3.0 in favor of recordEviction(weight)
|
|
||||||
recordEviction(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordEviction(int weight) {
|
|
||||||
evictionCount.mark();
|
|
||||||
evictionWeight.mark(weight);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CacheStats snapshot() {
|
|
||||||
return new CacheStats(
|
|
||||||
hitCount.getCount(),
|
|
||||||
missCount.getCount(),
|
|
||||||
loadSuccessCount.getCount(),
|
|
||||||
loadFailureCount.getCount(),
|
|
||||||
totalLoadTime.getCount(),
|
|
||||||
evictionCount.getCount(),
|
|
||||||
evictionWeight.getCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return snapshot().toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,39 +20,37 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public enum Operator {
|
public enum Operator {
|
||||||
|
EQ("=="),
|
||||||
|
|
||||||
EQ("=="),
|
IN("in"),
|
||||||
|
|
||||||
IN("in"),
|
GT(">"),
|
||||||
|
|
||||||
GT(">"),
|
LT("<"),
|
||||||
|
|
||||||
LT("<"),
|
GTE(">="),
|
||||||
|
|
||||||
GTE(">="),
|
LTE("<=");
|
||||||
|
|
||||||
LTE("<=");
|
private static final Map<String, Operator> indexByName = new HashMap<String, Operator>();
|
||||||
|
|
||||||
private final String name;
|
static {
|
||||||
|
for (Operator fo : Operator.values()) {
|
||||||
|
indexByName.put(fo.getName(), fo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final static Map<String, Operator> indexByName = new HashMap<String, Operator>();
|
private final String name;
|
||||||
|
|
||||||
static {
|
private Operator(String name) {
|
||||||
for (Operator fo : Operator.values()) {
|
this.name = name;
|
||||||
indexByName.put(fo.getName(), fo);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Operator(String name) {
|
public static Operator findByOperator(String name) {
|
||||||
this.name = name;
|
return indexByName.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operator findByOperator(String name) {
|
|
||||||
return indexByName.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.querybuilder.Ordering;
|
import com.datastax.driver.core.querybuilder.Ordering;
|
||||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
|
import java.util.Objects;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.ColumnType;
|
import net.helenus.mapping.ColumnType;
|
||||||
import net.helenus.mapping.MappingUtil;
|
import net.helenus.mapping.MappingUtil;
|
||||||
|
@ -13,37 +11,34 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class Ordered {
|
public final class Ordered {
|
||||||
|
|
||||||
private final Getter<?> getter;
|
private final Getter<?> getter;
|
||||||
private final OrderingDirection direction;
|
private final OrderingDirection direction;
|
||||||
|
|
||||||
public Ordered(Getter<?> getter, OrderingDirection direction) {
|
public Ordered(Getter<?> getter, OrderingDirection direction) {
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Ordering getOrdering() {
|
public Ordering getOrdering() {
|
||||||
|
|
||||||
Objects.requireNonNull(getter, "property is null");
|
Objects.requireNonNull(getter, "property is null");
|
||||||
Objects.requireNonNull(direction, "direction is null");
|
Objects.requireNonNull(direction, "direction is null");
|
||||||
|
|
||||||
HelenusPropertyNode propNode = MappingUtil.resolveMappingProperty(getter);
|
HelenusPropertyNode propNode = MappingUtil.resolveMappingProperty(getter);
|
||||||
|
|
||||||
if (propNode.getProperty().getColumnType() != ColumnType.CLUSTERING_COLUMN) {
|
if (propNode.getProperty().getColumnType() != ColumnType.CLUSTERING_COLUMN) {
|
||||||
throw new HelenusMappingException(
|
throw new HelenusMappingException(
|
||||||
"property must be a clustering column " + propNode.getProperty().getPropertyName());
|
"property must be a clustering column " + propNode.getProperty().getPropertyName());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
|
case ASC:
|
||||||
|
return QueryBuilder.asc(propNode.getColumnName());
|
||||||
|
|
||||||
case ASC :
|
case DESC:
|
||||||
return QueryBuilder.asc(propNode.getColumnName());
|
return QueryBuilder.desc(propNode.getColumnName());
|
||||||
|
}
|
||||||
case DESC :
|
|
||||||
return QueryBuilder.desc(propNode.getColumnName());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HelenusMappingException("invalid direction " + direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
throw new HelenusMappingException("invalid direction " + direction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
78
src/main/java/net/helenus/core/PostCommitFunction.java
Normal file
78
src/main/java/net/helenus/core/PostCommitFunction.java
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package net.helenus.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import net.helenus.support.CheckedRunnable;
|
||||||
|
|
||||||
|
public class PostCommitFunction<T, R> implements java.util.function.Function<T, R> {
|
||||||
|
public static final PostCommitFunction<Void, Void> NULL_ABORT = new PostCommitFunction<Void, Void>(null, null, null, false);
|
||||||
|
public static final PostCommitFunction<Void, Void> NULL_COMMIT = new PostCommitFunction<Void, Void>(null, null, null, true);
|
||||||
|
|
||||||
|
private final List<CheckedRunnable> commitThunks;
|
||||||
|
private final List<CheckedRunnable> abortThunks;
|
||||||
|
private Consumer<? super Throwable> exceptionallyThunk;
|
||||||
|
private boolean committed;
|
||||||
|
|
||||||
|
PostCommitFunction(List<CheckedRunnable> postCommit, List<CheckedRunnable> abortThunks,
|
||||||
|
Consumer<? super Throwable> exceptionallyThunk,
|
||||||
|
boolean committed) {
|
||||||
|
this.commitThunks = postCommit;
|
||||||
|
this.abortThunks = abortThunks;
|
||||||
|
this.exceptionallyThunk = exceptionallyThunk;
|
||||||
|
this.committed = committed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apply(CheckedRunnable... fns) {
|
||||||
|
try {
|
||||||
|
for (CheckedRunnable fn : fns) {
|
||||||
|
fn.run();
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
if (exceptionallyThunk != null) {
|
||||||
|
exceptionallyThunk.accept(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostCommitFunction<T, R> andThen(CheckedRunnable... after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
if (commitThunks == null) {
|
||||||
|
if (committed) {
|
||||||
|
apply(after);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (CheckedRunnable fn : after) {
|
||||||
|
commitThunks.add(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostCommitFunction<T, R> orElse(CheckedRunnable... after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
if (abortThunks == null) {
|
||||||
|
if (!committed) {
|
||||||
|
apply(after);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (CheckedRunnable fn : after) {
|
||||||
|
abortThunks.add(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostCommitFunction<T, R> exceptionally(Consumer<? super Throwable> fn) {
|
||||||
|
Objects.requireNonNull(fn);
|
||||||
|
exceptionallyThunk = fn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R apply(T t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,86 +18,84 @@ package net.helenus.core;
|
||||||
|
|
||||||
import com.datastax.driver.core.querybuilder.Clause;
|
import com.datastax.driver.core.querybuilder.Clause;
|
||||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
|
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.value.ColumnValuePreparer;
|
import net.helenus.mapping.value.ColumnValuePreparer;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class Postulate<V> {
|
public final class Postulate<V> {
|
||||||
|
|
||||||
private final Operator operator;
|
private final Operator operator;
|
||||||
private final V[] values;
|
private final V[] values;
|
||||||
|
|
||||||
protected Postulate(Operator op, V[] values) {
|
protected Postulate(Operator op, V[] values) {
|
||||||
this.operator = op;
|
this.operator = op;
|
||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> of(Operator op, V... values) {
|
public static <V> Postulate<V> of(Operator op, V... values) {
|
||||||
return new Postulate<V>(op, values);
|
return new Postulate<V>(op, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Clause getClause(HelenusPropertyNode node, ColumnValuePreparer valuePreparer) {
|
public Clause getClause(HelenusPropertyNode node, ColumnValuePreparer valuePreparer) {
|
||||||
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
|
case EQ:
|
||||||
|
return QueryBuilder.eq(
|
||||||
|
node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
||||||
|
|
||||||
case EQ :
|
case IN:
|
||||||
return QueryBuilder.eq(node.getColumnName(),
|
Object[] preparedValues = new Object[values.length];
|
||||||
valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
for (int i = 0; i != values.length; ++i) {
|
||||||
|
preparedValues[i] = valuePreparer.prepareColumnValue(values[i], node.getProperty());
|
||||||
|
}
|
||||||
|
return QueryBuilder.in(node.getColumnName(), preparedValues);
|
||||||
|
|
||||||
case IN :
|
case LT:
|
||||||
Object[] preparedValues = new Object[values.length];
|
return QueryBuilder.lt(
|
||||||
for (int i = 0; i != values.length; ++i) {
|
node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
||||||
preparedValues[i] = valuePreparer.prepareColumnValue(values[i], node.getProperty());
|
|
||||||
}
|
|
||||||
return QueryBuilder.in(node.getColumnName(), preparedValues);
|
|
||||||
|
|
||||||
case LT :
|
case LTE:
|
||||||
return QueryBuilder.lt(node.getColumnName(),
|
return QueryBuilder.lte(
|
||||||
valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
||||||
|
|
||||||
case LTE :
|
case GT:
|
||||||
return QueryBuilder.lte(node.getColumnName(),
|
return QueryBuilder.gt(
|
||||||
valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
||||||
|
|
||||||
case GT :
|
case GTE:
|
||||||
return QueryBuilder.gt(node.getColumnName(),
|
return QueryBuilder.gte(
|
||||||
valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
||||||
|
|
||||||
case GTE :
|
default:
|
||||||
return QueryBuilder.gte(node.getColumnName(),
|
throw new HelenusMappingException("unknown filter operation " + operator);
|
||||||
valuePreparer.prepareColumnValue(values[0], node.getProperty()));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default :
|
public V[] values() {
|
||||||
throw new HelenusMappingException("unknown filter operation " + operator);
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
@Override
|
if (operator == Operator.IN) {
|
||||||
public String toString() {
|
|
||||||
|
|
||||||
if (operator == Operator.IN) {
|
if (values == null) {
|
||||||
|
return "in()";
|
||||||
|
}
|
||||||
|
|
||||||
if (values == null) {
|
int len = values.length;
|
||||||
return "in()";
|
StringBuilder b = new StringBuilder();
|
||||||
}
|
b.append("in(");
|
||||||
|
for (int i = 0; i != len; i++) {
|
||||||
int len = values.length;
|
if (b.length() > 3) {
|
||||||
StringBuilder b = new StringBuilder();
|
b.append(", ");
|
||||||
b.append("in(");
|
}
|
||||||
for (int i = 0; i != len; i++) {
|
b.append(String.valueOf(values[i]));
|
||||||
if (b.length() > 3) {
|
}
|
||||||
b.append(", ");
|
return b.append(')').toString();
|
||||||
}
|
}
|
||||||
b.append(String.valueOf(values[i]));
|
|
||||||
}
|
|
||||||
return b.append(')').toString();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return operator.getName() + values[0];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return operator.getName() + values[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,6 +20,5 @@ import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
public interface PropertyValueValidator {
|
public interface PropertyValueValidator {
|
||||||
|
|
||||||
void validate(HelenusProperty prop, Object value);
|
void validate(HelenusProperty prop, Object value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,90 +16,80 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
|
import com.datastax.driver.core.querybuilder.BindMarker;
|
||||||
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import com.datastax.driver.core.querybuilder.BindMarker;
|
|
||||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
|
||||||
|
|
||||||
import net.helenus.mapping.OrderingDirection;
|
import net.helenus.mapping.OrderingDirection;
|
||||||
|
|
||||||
/**
|
/** Sugar methods for the queries */
|
||||||
* Sugar methods for the queries
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class Query {
|
public final class Query {
|
||||||
|
|
||||||
private Query() {
|
private Query() {}
|
||||||
}
|
|
||||||
|
|
||||||
public static BindMarker marker() {
|
public static BindMarker marker() {
|
||||||
return QueryBuilder.bindMarker();
|
return QueryBuilder.bindMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BindMarker marker(String name) {
|
public static BindMarker marker(String name) {
|
||||||
return QueryBuilder.bindMarker(name);
|
return QueryBuilder.bindMarker(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ordered asc(Getter<?> getter) {
|
public static Ordered asc(Getter<?> getter) {
|
||||||
return new Ordered(getter, OrderingDirection.ASC);
|
return new Ordered(getter, OrderingDirection.ASC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ordered desc(Getter<?> getter) {
|
public static Ordered desc(Getter<?> getter) {
|
||||||
return new Ordered(getter, OrderingDirection.DESC);
|
return new Ordered(getter, OrderingDirection.DESC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> eq(V val) {
|
public static <V> Postulate<V> eq(V val) {
|
||||||
return Postulate.of(Operator.EQ, val);
|
return Postulate.of(Operator.EQ, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> lt(V val) {
|
public static <V> Postulate<V> lt(V val) {
|
||||||
return Postulate.of(Operator.LT, val);
|
return Postulate.of(Operator.LT, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> lte(V val) {
|
public static <V> Postulate<V> lte(V val) {
|
||||||
return Postulate.of(Operator.LTE, val);
|
return Postulate.of(Operator.LTE, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> gt(V val) {
|
public static <V> Postulate<V> gt(V val) {
|
||||||
return Postulate.of(Operator.GT, val);
|
return Postulate.of(Operator.GT, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> gte(V val) {
|
public static <V> Postulate<V> gte(V val) {
|
||||||
return Postulate.of(Operator.GTE, val);
|
return Postulate.of(Operator.GTE, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> Postulate<V> in(V[] vals) {
|
public static <V> Postulate<V> in(V[] vals) {
|
||||||
return new Postulate<V>(Operator.IN, vals);
|
return new Postulate<V>(Operator.IN, vals);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <K, V> Getter<V> getIdx(Getter<List<V>> listGetter, int index) {
|
public static <K, V> Getter<V> getIdx(Getter<List<V>> listGetter, int index) {
|
||||||
Objects.requireNonNull(listGetter, "listGetter is null");
|
Objects.requireNonNull(listGetter, "listGetter is null");
|
||||||
|
|
||||||
return new Getter<V>() {
|
return new Getter<V>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get() {
|
public V get() {
|
||||||
return listGetter.get().get(index);
|
return listGetter.get().get(index);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
};
|
public static <K, V> Getter<V> get(Getter<Map<K, V>> mapGetter, K k) {
|
||||||
}
|
Objects.requireNonNull(mapGetter, "mapGetter is null");
|
||||||
|
Objects.requireNonNull(k, "key is null");
|
||||||
|
|
||||||
public static <K, V> Getter<V> get(Getter<Map<K, V>> mapGetter, K k) {
|
return new Getter<V>() {
|
||||||
Objects.requireNonNull(mapGetter, "mapGetter is null");
|
|
||||||
Objects.requireNonNull(k, "key is null");
|
|
||||||
|
|
||||||
return new Getter<V>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get() {
|
|
||||||
return mapGetter.get().get(k);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get() {
|
||||||
|
return mapGetter.get().get(k);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,351 +16,459 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.*;
|
import com.datastax.driver.core.*;
|
||||||
import com.datastax.driver.core.IndexMetadata;
|
import com.datastax.driver.core.querybuilder.IsNotNullClause;
|
||||||
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
|
import com.datastax.driver.core.querybuilder.Select;
|
||||||
import com.datastax.driver.core.schemabuilder.*;
|
import com.datastax.driver.core.schemabuilder.*;
|
||||||
import com.datastax.driver.core.schemabuilder.Create.Options;
|
import com.datastax.driver.core.schemabuilder.Create.Options;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.*;
|
import net.helenus.mapping.*;
|
||||||
import net.helenus.mapping.ColumnType;
|
import net.helenus.mapping.ColumnType;
|
||||||
|
import net.helenus.mapping.annotation.ClusteringColumn;
|
||||||
import net.helenus.mapping.type.OptionalColumnMetadata;
|
import net.helenus.mapping.type.OptionalColumnMetadata;
|
||||||
import net.helenus.support.CqlUtil;
|
import net.helenus.support.CqlUtil;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class SchemaUtil {
|
public final class SchemaUtil {
|
||||||
|
|
||||||
private SchemaUtil() {
|
private SchemaUtil() {}
|
||||||
}
|
|
||||||
|
|
||||||
public static RegularStatement use(String keyspace, boolean forceQuote) {
|
public static RegularStatement use(String keyspace, boolean forceQuote) {
|
||||||
if (forceQuote) {
|
if (forceQuote) {
|
||||||
return new SimpleStatement("USE" + CqlUtil.forceQuote(keyspace));
|
return new SimpleStatement("USE" + CqlUtil.forceQuote(keyspace));
|
||||||
} else {
|
} else {
|
||||||
return new SimpleStatement("USE " + keyspace);
|
return new SimpleStatement("USE " + keyspace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SchemaStatement createUserType(HelenusEntity entity) {
|
public static SchemaStatement createUserType(HelenusEntity entity) {
|
||||||
|
|
||||||
if (entity.getType() != HelenusEntityType.UDT) {
|
|
||||||
throw new HelenusMappingException("expected UDT entity " + entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateType create = SchemaBuilder.createType(entity.getName().toCql());
|
|
||||||
|
|
||||||
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
|
||||||
|
|
||||||
ColumnType columnType = prop.getColumnType();
|
|
||||||
|
|
||||||
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
|
|
||||||
throw new HelenusMappingException("primary key columns are not supported in UserDefinedType for "
|
|
||||||
+ prop.getPropertyName() + " in entity " + entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
prop.getDataType().addColumn(create, prop.getColumnName());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new HelenusMappingException("invalid column name '" + prop.getColumnName() + "' in entity '"
|
|
||||||
+ entity.getName().getName() + "'", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return create;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<SchemaStatement> alterUserType(UserType userType, HelenusEntity entity,
|
|
||||||
boolean dropUnusedColumns) {
|
|
||||||
|
|
||||||
if (entity.getType() != HelenusEntityType.UDT) {
|
|
||||||
throw new HelenusMappingException("expected UDT entity " + entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: In future replace SchemaBuilder.alterTable by SchemaBuilder.alterType
|
|
||||||
* when it will exist
|
|
||||||
*/
|
|
||||||
|
|
||||||
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
|
|
||||||
|
|
||||||
final Set<String> visitedColumns = dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
|
|
||||||
|
|
||||||
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
|
||||||
|
|
||||||
String columnName = prop.getColumnName().getName();
|
|
||||||
|
|
||||||
if (dropUnusedColumns) {
|
|
||||||
visitedColumns.add(columnName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnType columnType = prop.getColumnType();
|
|
||||||
|
|
||||||
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataType dataType = userType.getFieldType(columnName);
|
|
||||||
SchemaStatement stmt = prop.getDataType().alterColumn(alter, prop.getColumnName(),
|
|
||||||
optional(columnName, dataType));
|
|
||||||
|
|
||||||
if (stmt != null) {
|
|
||||||
result.add(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dropUnusedColumns) {
|
|
||||||
for (String field : userType.getFieldNames()) {
|
|
||||||
if (!visitedColumns.contains(field)) {
|
|
||||||
|
|
||||||
result.add(alter.dropColumn(field));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SchemaStatement dropUserType(HelenusEntity entity) {
|
|
||||||
|
|
||||||
if (entity.getType() != HelenusEntityType.UDT) {
|
|
||||||
throw new HelenusMappingException("expected UDT entity " + entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SchemaBuilder.dropType(entity.getName().toCql()).ifExists();
|
|
||||||
|
|
||||||
|
if (entity.getType() != HelenusEntityType.UDT) {
|
||||||
|
throw new HelenusMappingException("expected UDT entity " + entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SchemaStatement dropUserType(UserType type) {
|
CreateType create = SchemaBuilder.createType(entity.getName().toCql());
|
||||||
|
|
||||||
return SchemaBuilder.dropType(type.getTypeName()).ifExists();
|
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
||||||
}
|
|
||||||
|
|
||||||
public static SchemaStatement createTable(HelenusEntity entity) {
|
ColumnType columnType = prop.getColumnType();
|
||||||
|
|
||||||
if (entity.getType() != HelenusEntityType.TABLE) {
|
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
|
||||||
throw new HelenusMappingException("expected table entity " + entity);
|
throw new HelenusMappingException(
|
||||||
}
|
"primary key columns are not supported in UserDefinedType for "
|
||||||
|
+ prop.getPropertyName()
|
||||||
|
+ " in entity "
|
||||||
|
+ entity);
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: There is a bug in the normal path of createTable where the
|
try {
|
||||||
// "cache" is set too early and never unset preventing more than
|
prop.getDataType().addColumn(create, prop.getColumnName());
|
||||||
// one column on a table.
|
} catch (IllegalArgumentException e) {
|
||||||
// SchemaBuilder.createTable(entity.getName().toCql());
|
throw new HelenusMappingException(
|
||||||
CreateTable create = new CreateTable(entity.getName().toCql());
|
"invalid column name '"
|
||||||
|
+ prop.getColumnName()
|
||||||
|
+ "' in entity '"
|
||||||
|
+ entity.getName().getName()
|
||||||
|
+ "'",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
create.ifNotExists();
|
return create;
|
||||||
|
}
|
||||||
|
|
||||||
List<HelenusProperty> clusteringColumns = new ArrayList<HelenusProperty>();
|
public static List<SchemaStatement> alterUserType(
|
||||||
|
UserType userType, HelenusEntity entity, boolean dropUnusedColumns) {
|
||||||
|
|
||||||
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
if (entity.getType() != HelenusEntityType.UDT) {
|
||||||
|
throw new HelenusMappingException("expected UDT entity " + entity);
|
||||||
|
}
|
||||||
|
|
||||||
ColumnType columnType = prop.getColumnType();
|
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
|
||||||
|
|
||||||
if (columnType == ColumnType.CLUSTERING_COLUMN) {
|
/**
|
||||||
clusteringColumns.add(prop);
|
* TODO: In future replace SchemaBuilder.alterTable by SchemaBuilder.alterType when it will
|
||||||
}
|
* exist
|
||||||
|
*/
|
||||||
|
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
|
||||||
|
|
||||||
prop.getDataType().addColumn(create, prop.getColumnName());
|
final Set<String> visitedColumns =
|
||||||
|
dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
|
||||||
|
|
||||||
}
|
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
||||||
|
|
||||||
if (!clusteringColumns.isEmpty()) {
|
String columnName = prop.getColumnName().getName();
|
||||||
Options options = create.withOptions();
|
|
||||||
clusteringColumns
|
|
||||||
.forEach(p -> options.clusteringOrder(p.getColumnName().toCql(), mapDirection(p.getOrdering())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return create;
|
if (dropUnusedColumns) {
|
||||||
|
visitedColumns.add(columnName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
ColumnType columnType = prop.getColumnType();
|
||||||
|
|
||||||
public static List<SchemaStatement> alterTable(TableMetadata tmd, HelenusEntity entity, boolean dropUnusedColumns) {
|
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (entity.getType() != HelenusEntityType.TABLE) {
|
DataType dataType = userType.getFieldType(columnName);
|
||||||
throw new HelenusMappingException("expected table entity " + entity);
|
SchemaStatement stmt =
|
||||||
}
|
prop.getDataType()
|
||||||
|
.alterColumn(alter, prop.getColumnName(), optional(columnName, dataType));
|
||||||
|
|
||||||
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
|
if (stmt != null) {
|
||||||
|
result.add(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
|
if (dropUnusedColumns) {
|
||||||
|
for (String field : userType.getFieldNames()) {
|
||||||
|
if (!visitedColumns.contains(field)) {
|
||||||
|
|
||||||
final Set<String> visitedColumns = dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
|
result.add(alter.dropColumn(field));
|
||||||
|
|
||||||
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
|
||||||
|
|
||||||
String columnName = prop.getColumnName().getName();
|
|
||||||
|
|
||||||
if (dropUnusedColumns) {
|
|
||||||
visitedColumns.add(columnName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnType columnType = prop.getColumnType();
|
|
||||||
|
|
||||||
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnMetadata columnMetadata = tmd.getColumn(columnName);
|
|
||||||
SchemaStatement stmt = prop.getDataType().alterColumn(alter, prop.getColumnName(),
|
|
||||||
optional(columnMetadata));
|
|
||||||
|
|
||||||
if (stmt != null) {
|
|
||||||
result.add(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dropUnusedColumns) {
|
|
||||||
for (ColumnMetadata cm : tmd.getColumns()) {
|
|
||||||
if (!visitedColumns.contains(cm.getName())) {
|
|
||||||
|
|
||||||
result.add(alter.dropColumn(cm.getName()));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SchemaStatement dropTable(HelenusEntity entity) {
|
|
||||||
|
|
||||||
if (entity.getType() != HelenusEntityType.TABLE) {
|
|
||||||
throw new HelenusMappingException("expected table entity " + entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SchemaBuilder.dropTable(entity.getName().toCql()).ifExists();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SchemaStatement createIndex(HelenusProperty prop) {
|
|
||||||
if (prop.caseSensitiveIndex()) {
|
|
||||||
return SchemaBuilder.createIndex(prop.getIndexName().get().toCql())
|
|
||||||
.ifNotExists()
|
|
||||||
.onTable(prop.getEntity().getName().toCql())
|
|
||||||
.andColumn(prop.getColumnName().toCql());
|
|
||||||
} else {
|
|
||||||
return new CreateSasiIndex(prop.getIndexName().get().toCql())
|
|
||||||
.ifNotExists()
|
|
||||||
.onTable(prop.getEntity().getName().toCql())
|
|
||||||
.andColumn(prop.getColumnName().toCql());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static List<SchemaStatement> createIndexes(HelenusEntity entity) {
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return entity.getOrderedProperties().stream().filter(p -> p.getIndexName().isPresent())
|
public static SchemaStatement dropUserType(HelenusEntity entity) {
|
||||||
.map(p -> SchemaUtil.createIndex(p)).collect(Collectors.toList());
|
|
||||||
|
|
||||||
}
|
if (entity.getType() != HelenusEntityType.UDT) {
|
||||||
|
throw new HelenusMappingException("expected UDT entity " + entity);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<SchemaStatement> alterIndexes(TableMetadata tmd, HelenusEntity entity,
|
return SchemaBuilder.dropType(entity.getName().toCql()).ifExists();
|
||||||
boolean dropUnusedIndexes) {
|
}
|
||||||
|
|
||||||
List<SchemaStatement> list = new ArrayList<SchemaStatement>();
|
public static SchemaStatement dropUserType(UserType type) {
|
||||||
|
|
||||||
final Set<String> visitedColumns = dropUnusedIndexes ? new HashSet<String>() : Collections.<String>emptySet();
|
return SchemaBuilder.dropType(type.getTypeName()).ifExists();
|
||||||
|
}
|
||||||
|
|
||||||
entity.getOrderedProperties().stream().filter(p -> p.getIndexName().isPresent()).forEach(p -> {
|
public static String createPrimaryKeyPhrase(Collection<HelenusProperty> properties) {
|
||||||
|
List<String> p = new ArrayList<String>(properties.size());
|
||||||
|
List<String> c = new ArrayList<String>(properties.size());
|
||||||
|
|
||||||
String columnName = p.getColumnName().getName();
|
for (HelenusProperty prop : properties) {
|
||||||
|
String columnName = prop.getColumnName().toCql();
|
||||||
|
switch (prop.getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
p.add(columnName);
|
||||||
|
break;
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
c.add(columnName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dropUnusedIndexes) {
|
if (p.size() == 0 && c.size() == 0)
|
||||||
visitedColumns.add(columnName);
|
return "{"
|
||||||
}
|
+ properties
|
||||||
|
.stream()
|
||||||
|
.map(HelenusProperty::getPropertyName)
|
||||||
|
.collect(Collectors.joining(", "))
|
||||||
|
+ "}";
|
||||||
|
|
||||||
ColumnMetadata cm = tmd.getColumn(columnName);
|
return "("
|
||||||
|
+ ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0))
|
||||||
|
+ ((c.size() > 0)
|
||||||
|
? ", " + ((c.size() > 1) ? "(" + String.join(", ", c) + ")" : c.get(0))
|
||||||
|
: "")
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
|
||||||
if (cm != null) {
|
public static SchemaStatement createMaterializedView(
|
||||||
IndexMetadata im = tmd.getIndex(columnName);
|
String keyspace, String viewName, HelenusEntity entity) {
|
||||||
if (im == null) {
|
if (entity.getType() != HelenusEntityType.VIEW) {
|
||||||
list.add(createIndex(p));
|
throw new HelenusMappingException("expected view entity " + entity);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
list.add(createIndex(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
|
||||||
|
entity
|
||||||
|
.getOrderedProperties()
|
||||||
|
.stream()
|
||||||
|
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
|
||||||
|
.forEach(p -> props.add(p));
|
||||||
|
|
||||||
if (dropUnusedIndexes) {
|
Select.Selection selection = QueryBuilder.select();
|
||||||
|
|
||||||
tmd.getColumns().stream().filter(c -> tmd.getIndex(c.getName()) != null && !visitedColumns.contains(c.getName()))
|
for (HelenusPropertyNode prop : props) {
|
||||||
.forEach(c -> {
|
String columnName = prop.getColumnName();
|
||||||
list.add(SchemaBuilder.dropIndex(tmd.getIndex(c.getName()).getName()).ifExists());
|
selection = selection.column(columnName);
|
||||||
|
}
|
||||||
|
Class<?> iface = entity.getMappingInterface();
|
||||||
|
String tableName = Helenus.entity(iface.getInterfaces()[0]).getName().toCql();
|
||||||
|
Select.Where where = selection.from(tableName).where();
|
||||||
|
List<String> o = new ArrayList<String>(props.size());
|
||||||
|
|
||||||
});
|
for (HelenusPropertyNode prop : props) {
|
||||||
|
String columnName = prop.getColumnName();
|
||||||
|
switch (prop.getProperty().getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
where = where.and(new IsNotNullClause(columnName));
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
case CLUSTERING_COLUMN:
|
||||||
|
where = where.and(new IsNotNullClause(columnName));
|
||||||
|
|
||||||
return list;
|
ClusteringColumn clusteringColumn =
|
||||||
|
prop.getProperty().getGetterMethod().getAnnotation(ClusteringColumn.class);
|
||||||
|
if (clusteringColumn != null && clusteringColumn.ordering() != null) {
|
||||||
|
o.add(columnName + " " + clusteringColumn.ordering().cql());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
String primaryKey = "PRIMARY KEY " + createPrimaryKeyPhrase(entity.getOrderedProperties());
|
||||||
|
|
||||||
public static SchemaStatement dropIndex(HelenusProperty prop) {
|
String clustering = "";
|
||||||
return SchemaBuilder.dropIndex(prop.getIndexName().get().toCql()).ifExists();
|
if (o.size() > 0) {
|
||||||
}
|
clustering = "WITH CLUSTERING ORDER BY (" + String.join(", ", o) + ")";
|
||||||
|
}
|
||||||
|
return new CreateMaterializedView(keyspace, viewName, where, primaryKey, clustering)
|
||||||
|
.ifNotExists();
|
||||||
|
}
|
||||||
|
|
||||||
private static SchemaBuilder.Direction mapDirection(OrderingDirection o) {
|
public static SchemaStatement dropMaterializedView(
|
||||||
switch (o) {
|
String keyspace, String viewName, HelenusEntity entity) {
|
||||||
case ASC :
|
return new DropMaterializedView(keyspace, viewName);
|
||||||
return SchemaBuilder.Direction.ASC;
|
}
|
||||||
case DESC :
|
|
||||||
return SchemaBuilder.Direction.DESC;
|
|
||||||
}
|
|
||||||
throw new HelenusMappingException("unknown ordering " + o);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void throwNoMapping(HelenusProperty prop) {
|
public static SchemaStatement createTable(HelenusEntity entity) {
|
||||||
|
|
||||||
throw new HelenusMappingException(
|
if (entity.getType() != HelenusEntityType.TABLE) {
|
||||||
"only primitive types and Set,List,Map collections and UserDefinedTypes are allowed, unknown type for property '"
|
throw new HelenusMappingException("expected table entity " + entity);
|
||||||
+ prop.getPropertyName() + "' type is '" + prop.getJavaType() + "' in the entity "
|
}
|
||||||
+ prop.getEntity());
|
|
||||||
|
|
||||||
}
|
// NOTE: There is a bug in the normal path of createTable where the
|
||||||
|
// "cache" is set too early and never unset preventing more than
|
||||||
|
// one column on a table.
|
||||||
|
// SchemaBuilder.createTable(entity.getName().toCql());
|
||||||
|
CreateTable create = new CreateTable(entity.getName().toCql());
|
||||||
|
|
||||||
private static OptionalColumnMetadata optional(final ColumnMetadata columnMetadata) {
|
create.ifNotExists();
|
||||||
if (columnMetadata != null) {
|
|
||||||
return new OptionalColumnMetadata() {
|
|
||||||
|
|
||||||
@Override
|
List<HelenusProperty> clusteringColumns = new ArrayList<HelenusProperty>();
|
||||||
public String getName() {
|
|
||||||
return columnMetadata.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
||||||
public DataType getType() {
|
|
||||||
return columnMetadata.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
ColumnType columnType = prop.getColumnType();
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OptionalColumnMetadata optional(final String name, final DataType dataType) {
|
if (columnType == ColumnType.CLUSTERING_COLUMN) {
|
||||||
if (dataType != null) {
|
clusteringColumns.add(prop);
|
||||||
return new OptionalColumnMetadata() {
|
}
|
||||||
|
|
||||||
@Override
|
prop.getDataType().addColumn(create, prop.getColumnName());
|
||||||
public String getName() {
|
}
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (!clusteringColumns.isEmpty()) {
|
||||||
public DataType getType() {
|
Options options = create.withOptions();
|
||||||
return dataType;
|
clusteringColumns.forEach(
|
||||||
}
|
p -> options.clusteringOrder(p.getColumnName().toCql(), mapDirection(p.getOrdering())));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
return create;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
public static List<SchemaStatement> alterTable(
|
||||||
|
TableMetadata tmd, HelenusEntity entity, boolean dropUnusedColumns) {
|
||||||
|
|
||||||
|
if (entity.getType() != HelenusEntityType.TABLE) {
|
||||||
|
throw new HelenusMappingException("expected table entity " + entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
|
||||||
|
|
||||||
|
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
|
||||||
|
|
||||||
|
final Set<String> visitedColumns =
|
||||||
|
dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
|
||||||
|
|
||||||
|
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
||||||
|
|
||||||
|
String columnName = prop.getColumnName().getName();
|
||||||
|
|
||||||
|
if (dropUnusedColumns) {
|
||||||
|
visitedColumns.add(columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnType columnType = prop.getColumnType();
|
||||||
|
|
||||||
|
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnMetadata columnMetadata = tmd.getColumn(columnName);
|
||||||
|
SchemaStatement stmt =
|
||||||
|
prop.getDataType().alterColumn(alter, prop.getColumnName(), optional(columnMetadata));
|
||||||
|
|
||||||
|
if (stmt != null) {
|
||||||
|
result.add(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropUnusedColumns) {
|
||||||
|
for (ColumnMetadata cm : tmd.getColumns()) {
|
||||||
|
if (!visitedColumns.contains(cm.getName())) {
|
||||||
|
|
||||||
|
result.add(alter.dropColumn(cm.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchemaStatement dropTable(HelenusEntity entity) {
|
||||||
|
|
||||||
|
if (entity.getType() != HelenusEntityType.TABLE) {
|
||||||
|
throw new HelenusMappingException("expected table entity " + entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SchemaBuilder.dropTable(entity.getName().toCql()).ifExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchemaStatement createIndex(HelenusProperty prop) {
|
||||||
|
if (prop.caseSensitiveIndex()) {
|
||||||
|
return SchemaBuilder.createIndex(indexName(prop))
|
||||||
|
.ifNotExists()
|
||||||
|
.onTable(prop.getEntity().getName().toCql())
|
||||||
|
.andColumn(prop.getColumnName().toCql());
|
||||||
|
} else {
|
||||||
|
return new CreateSasiIndex(prop.getIndexName().get().toCql())
|
||||||
|
.ifNotExists()
|
||||||
|
.onTable(prop.getEntity().getName().toCql())
|
||||||
|
.andColumn(prop.getColumnName().toCql());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchemaStatement> createIndexes(HelenusEntity entity) {
|
||||||
|
|
||||||
|
return entity
|
||||||
|
.getOrderedProperties()
|
||||||
|
.stream()
|
||||||
|
.filter(p -> p.getIndexName().isPresent())
|
||||||
|
.map(p -> SchemaUtil.createIndex(p))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchemaStatement> alterIndexes(
|
||||||
|
TableMetadata tmd, HelenusEntity entity, boolean dropUnusedIndexes) {
|
||||||
|
|
||||||
|
List<SchemaStatement> list = new ArrayList<SchemaStatement>();
|
||||||
|
|
||||||
|
final Set<String> visitedColumns =
|
||||||
|
dropUnusedIndexes ? new HashSet<String>() : Collections.<String>emptySet();
|
||||||
|
|
||||||
|
entity
|
||||||
|
.getOrderedProperties()
|
||||||
|
.stream()
|
||||||
|
.filter(p -> p.getIndexName().isPresent())
|
||||||
|
.forEach(
|
||||||
|
p -> {
|
||||||
|
String columnName = p.getColumnName().getName();
|
||||||
|
|
||||||
|
if (dropUnusedIndexes) {
|
||||||
|
visitedColumns.add(columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnMetadata cm = tmd.getColumn(columnName);
|
||||||
|
|
||||||
|
if (cm != null) {
|
||||||
|
IndexMetadata im = tmd.getIndex(columnName);
|
||||||
|
if (im == null) {
|
||||||
|
list.add(createIndex(p));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.add(createIndex(p));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dropUnusedIndexes) {
|
||||||
|
|
||||||
|
tmd.getColumns()
|
||||||
|
.stream()
|
||||||
|
.filter(c -> tmd.getIndex(c.getName()) != null && !visitedColumns.contains(c.getName()))
|
||||||
|
.forEach(
|
||||||
|
c -> {
|
||||||
|
list.add(SchemaBuilder.dropIndex(tmd.getIndex(c.getName()).getName()).ifExists());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchemaStatement dropIndex(HelenusProperty prop) {
|
||||||
|
return SchemaBuilder.dropIndex(indexName(prop)).ifExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SchemaBuilder.Direction mapDirection(OrderingDirection o) {
|
||||||
|
switch (o) {
|
||||||
|
case ASC:
|
||||||
|
return SchemaBuilder.Direction.ASC;
|
||||||
|
case DESC:
|
||||||
|
return SchemaBuilder.Direction.DESC;
|
||||||
|
}
|
||||||
|
throw new HelenusMappingException("unknown ordering " + o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void throwNoMapping(HelenusProperty prop) {
|
||||||
|
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"only primitive types and Set,List,Map collections and UserDefinedTypes are allowed, unknown type for property '"
|
||||||
|
+ prop.getPropertyName()
|
||||||
|
+ "' type is '"
|
||||||
|
+ prop.getJavaType()
|
||||||
|
+ "' in the entity "
|
||||||
|
+ prop.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OptionalColumnMetadata optional(final ColumnMetadata columnMetadata) {
|
||||||
|
if (columnMetadata != null) {
|
||||||
|
return new OptionalColumnMetadata() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return columnMetadata.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType getType() {
|
||||||
|
return columnMetadata.getType();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OptionalColumnMetadata optional(final String name, final DataType dataType) {
|
||||||
|
if (dataType != null) {
|
||||||
|
return new OptionalColumnMetadata() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType getType() {
|
||||||
|
return dataType;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String indexName(HelenusProperty prop) {
|
||||||
|
return prop.getEntity().getName().toCql() + "_" + prop.getIndexName().get().toCql();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,325 +19,447 @@ package net.helenus.core;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.datastax.driver.core.*;
|
import com.datastax.driver.core.*;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
|
||||||
import net.helenus.mapping.HelenusEntityType;
|
|
||||||
import net.helenus.mapping.value.ColumnValuePreparer;
|
|
||||||
import net.helenus.mapping.value.ColumnValueProvider;
|
|
||||||
import net.helenus.support.HelenusException;
|
|
||||||
import net.helenus.support.PackageUtil;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import javax.cache.CacheManager;
|
||||||
|
import net.helenus.core.reflect.DslExportable;
|
||||||
|
import net.helenus.mapping.HelenusEntity;
|
||||||
|
import net.helenus.mapping.HelenusEntityType;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
|
import net.helenus.mapping.value.ColumnValuePreparer;
|
||||||
|
import net.helenus.mapping.value.ColumnValueProvider;
|
||||||
|
import net.helenus.support.Either;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
|
import net.helenus.support.PackageUtil;
|
||||||
|
|
||||||
public final class SessionInitializer extends AbstractSessionOperations {
|
public final class SessionInitializer extends AbstractSessionOperations {
|
||||||
|
|
||||||
private final Session session;
|
private final Session session;
|
||||||
private CodecRegistry registry;
|
private final List<Either<Object, Class<?>>> initList = new ArrayList<Either<Object, Class<?>>>();
|
||||||
private MetricRegistry metricRegistry;
|
private CodecRegistry registry;
|
||||||
private String usingKeyspace;
|
private String usingKeyspace;
|
||||||
private boolean showCql = false;
|
private boolean showCql = false;
|
||||||
private PrintStream printStream = System.out;
|
private boolean showValues = true;
|
||||||
private Executor executor = MoreExecutors.directExecutor();
|
private ConsistencyLevel consistencyLevel;
|
||||||
|
private boolean idempotent = false;
|
||||||
|
private MetricRegistry metricRegistry = new MetricRegistry();
|
||||||
|
private PrintStream printStream = System.out;
|
||||||
|
private Executor executor = MoreExecutors.directExecutor();
|
||||||
|
private SessionRepositoryBuilder sessionRepository;
|
||||||
|
private boolean dropUnusedColumns = false;
|
||||||
|
private boolean dropUnusedIndexes = false;
|
||||||
|
private KeyspaceMetadata keyspaceMetadata;
|
||||||
|
private AutoDdl autoDdl = AutoDdl.UPDATE;
|
||||||
|
private CacheManager cacheManager = null;
|
||||||
|
|
||||||
private SessionRepositoryBuilder sessionRepository;
|
SessionInitializer(Session session, String keyspace) {
|
||||||
|
this.session = session;
|
||||||
private boolean dropUnusedColumns = false;
|
this.usingKeyspace = keyspace;
|
||||||
private boolean dropUnusedIndexes = false;
|
if (session != null) {
|
||||||
|
this.sessionRepository = new SessionRepositoryBuilder(session);
|
||||||
private KeyspaceMetadata keyspaceMetadata;
|
|
||||||
|
|
||||||
private final List<Object> initList = new ArrayList<Object>();
|
|
||||||
private AutoDdl autoDdl = AutoDdl.UPDATE;
|
|
||||||
|
|
||||||
SessionInitializer(Session session) {
|
|
||||||
this.session = Objects.requireNonNull(session, "empty session");
|
|
||||||
this.usingKeyspace = session.getLoggedKeyspace(); // can be null
|
|
||||||
this.sessionRepository = new SessionRepositoryBuilder(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cache(String key, Object value) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Session currentSession() {
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String usingKeyspace() {
|
|
||||||
return usingKeyspace;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Executor getExecutor() {
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SessionRepository getSessionRepository() {
|
|
||||||
throw new HelenusException("not expected to call");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ColumnValueProvider getValueProvider() {
|
|
||||||
throw new HelenusException("not expected to call");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ColumnValuePreparer getValuePreparer() {
|
|
||||||
throw new HelenusException("not expected to call");
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer showCql() {
|
|
||||||
this.showCql = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer showCql(boolean enabled) {
|
|
||||||
this.showCql = enabled;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PrintStream getPrintStream() {
|
|
||||||
return printStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer printTo(PrintStream out) {
|
|
||||||
this.printStream = out;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer withExecutor(Executor executor) {
|
|
||||||
Objects.requireNonNull(executor, "empty executor");
|
|
||||||
this.executor = executor;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer withMetricsRegistry(MetricRegistry metricRegistry) {
|
|
||||||
Objects.requireNonNull(metricRegistry, "empty registry");
|
|
||||||
this.metricRegistry = metricRegistry;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SessionInitializer withCachingExecutor() {
|
SessionInitializer(Session session) {
|
||||||
this.executor = Executors.newCachedThreadPool();
|
this.session = Objects.requireNonNull(session, "empty session");
|
||||||
return this;
|
this.usingKeyspace = session.getLoggedKeyspace(); // can be null
|
||||||
}
|
this.sessionRepository = new SessionRepositoryBuilder(session);
|
||||||
|
}
|
||||||
|
|
||||||
public SessionInitializer dropUnusedColumns(boolean enabled) {
|
@Override
|
||||||
this.dropUnusedColumns = enabled;
|
public Session currentSession() {
|
||||||
return this;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionInitializer dropUnusedIndexes(boolean enabled) {
|
@Override
|
||||||
this.dropUnusedIndexes = enabled;
|
public String usingKeyspace() {
|
||||||
return this;
|
return usingKeyspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionInitializer withCodecRegistry(CodecRegistry registry) {
|
@Override
|
||||||
this.registry = registry;
|
public Executor getExecutor() {
|
||||||
return this;
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionRepository getSessionRepository() {
|
||||||
|
throw new HelenusException("not expected to call");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColumnValueProvider getValueProvider() {
|
||||||
|
throw new HelenusException("not expected to call");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColumnValuePreparer getValuePreparer() {
|
||||||
|
throw new HelenusException("not expected to call");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer showCql() {
|
||||||
|
this.showCql = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer showCql(boolean enabled) {
|
||||||
|
this.showCql = enabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer showQueryValuesInLog(boolean showValues) {
|
||||||
|
this.showValues = showValues;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer showQueryValuesInLog() {
|
||||||
|
this.showValues = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean showValues() {
|
||||||
|
return showValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer metricRegistry(MetricRegistry metricRegistry) {
|
||||||
|
this.metricRegistry = metricRegistry;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer consistencyLevel(ConsistencyLevel consistencyLevel) {
|
||||||
|
this.consistencyLevel = consistencyLevel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer setCacheManager(CacheManager cacheManager) {
|
||||||
|
this.cacheManager = cacheManager;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConsistencyLevel getDefaultConsistencyLevel() {
|
||||||
|
return consistencyLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer setOperationsIdempotentByDefault() {
|
||||||
|
this.idempotent = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer idempotentQueryExecution(boolean idempotent) {
|
||||||
|
this.idempotent = idempotent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDefaultQueryIdempotency() {
|
||||||
|
return idempotent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrintStream getPrintStream() {
|
||||||
|
return printStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer printTo(PrintStream out) {
|
||||||
|
this.printStream = out;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer withExecutor(Executor executor) {
|
||||||
|
Objects.requireNonNull(executor, "empty executor");
|
||||||
|
this.executor = executor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer withCachingExecutor() {
|
||||||
|
this.executor = Executors.newCachedThreadPool();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer dropUnusedColumns(boolean enabled) {
|
||||||
|
this.dropUnusedColumns = enabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer dropUnusedIndexes(boolean enabled) {
|
||||||
|
this.dropUnusedIndexes = enabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer withCodecRegistry(CodecRegistry registry) {
|
||||||
|
this.registry = registry;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShowCql() {
|
||||||
|
return showCql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionInitializer addPackage(String packageName) {
|
||||||
|
try {
|
||||||
|
PackageUtil.getClasses(packageName)
|
||||||
|
.stream()
|
||||||
|
.filter(c -> c.isInterface() && !c.isAnnotation())
|
||||||
|
.forEach(
|
||||||
|
clazz -> {
|
||||||
|
initList.add(Either.right(clazz));
|
||||||
|
});
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
throw new HelenusException("fail to add package " + packageName, e);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public SessionInitializer add(Object... dsls) {
|
||||||
public boolean isShowCql() {
|
Objects.requireNonNull(dsls, "dsls is empty");
|
||||||
return showCql;
|
int len = dsls.length;
|
||||||
}
|
for (int i = 0; i != len; ++i) {
|
||||||
|
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
|
||||||
|
initList.add(Either.left(obj));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SessionInitializer addPackage(String packageName) {
|
public SessionInitializer autoValidate() {
|
||||||
try {
|
this.autoDdl = AutoDdl.VALIDATE;
|
||||||
PackageUtil.getClasses(packageName).stream().filter(c -> c.isInterface() && !c.isAnnotation())
|
return this;
|
||||||
.forEach(initList::add);
|
}
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
|
||||||
throw new HelenusException("fail to add package " + packageName, e);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer add(Object... dsls) {
|
public SessionInitializer autoUpdate() {
|
||||||
Objects.requireNonNull(dsls, "dsls is empty");
|
this.autoDdl = AutoDdl.UPDATE;
|
||||||
int len = dsls.length;
|
return this;
|
||||||
for (int i = 0; i != len; ++i) {
|
}
|
||||||
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
|
|
||||||
initList.add(obj);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer autoValidate() {
|
public SessionInitializer autoCreate() {
|
||||||
this.autoDdl = AutoDdl.VALIDATE;
|
this.autoDdl = AutoDdl.CREATE;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionInitializer autoUpdate() {
|
public SessionInitializer autoCreateDrop() {
|
||||||
this.autoDdl = AutoDdl.UPDATE;
|
this.autoDdl = AutoDdl.CREATE_DROP;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionInitializer autoCreate() {
|
public SessionInitializer auto(AutoDdl autoDdl) {
|
||||||
this.autoDdl = AutoDdl.CREATE;
|
this.autoDdl = autoDdl;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionInitializer autoCreateDrop() {
|
public SessionInitializer use(String keyspace) {
|
||||||
this.autoDdl = AutoDdl.CREATE_DROP;
|
if (session != null) {
|
||||||
return this;
|
session.execute(SchemaUtil.use(keyspace, false));
|
||||||
}
|
this.usingKeyspace = keyspace;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SessionInitializer auto(AutoDdl autoDdl) {
|
public SessionInitializer use(String keyspace, boolean forceQuote) {
|
||||||
this.autoDdl = autoDdl;
|
session.execute(SchemaUtil.use(keyspace, forceQuote));
|
||||||
return this;
|
this.usingKeyspace = keyspace;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SessionInitializer use(String keyspace) {
|
public void singleton() {
|
||||||
session.execute(SchemaUtil.use(keyspace, false));
|
Helenus.setSession(get());
|
||||||
this.usingKeyspace = keyspace;
|
}
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionInitializer use(String keyspace, boolean forceQuote) {
|
public synchronized HelenusSession get() {
|
||||||
session.execute(SchemaUtil.use(keyspace, forceQuote));
|
initialize();
|
||||||
this.usingKeyspace = keyspace;
|
return new HelenusSession(
|
||||||
return this;
|
session,
|
||||||
}
|
usingKeyspace,
|
||||||
|
registry,
|
||||||
|
showCql,
|
||||||
|
showValues,
|
||||||
|
printStream,
|
||||||
|
sessionRepository,
|
||||||
|
executor,
|
||||||
|
autoDdl == AutoDdl.CREATE_DROP,
|
||||||
|
consistencyLevel,
|
||||||
|
idempotent,
|
||||||
|
cacheManager,
|
||||||
|
metricRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
public void singleton() {
|
private void initialize() {
|
||||||
|
|
||||||
Helenus.setSession(get());
|
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized HelenusSession get() {
|
initList.forEach(
|
||||||
initialize();
|
(either) -> {
|
||||||
return new HelenusSession(session, usingKeyspace, registry, showCql, printStream, sessionRepository, executor,
|
Class<?> iface = null;
|
||||||
autoDdl == AutoDdl.CREATE_DROP, metricRegistry);
|
if (either.isLeft()) {
|
||||||
}
|
iface = MappingUtil.getMappingInterface(either.getLeft());
|
||||||
|
} else {
|
||||||
|
iface = either.getRight();
|
||||||
|
}
|
||||||
|
|
||||||
private void initialize() {
|
DslExportable dsl = (DslExportable) Helenus.dsl(iface);
|
||||||
|
if (session != null) {
|
||||||
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
|
dsl.setCassandraMetadataForHelenusSession(session.getCluster().getMetadata());
|
||||||
|
}
|
||||||
initList.forEach(dsl -> sessionRepository.add(dsl));
|
if (sessionRepository != null) {
|
||||||
|
sessionRepository.add(dsl);
|
||||||
TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
|
}
|
||||||
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);
|
|
||||||
|
|
||||||
switch (autoDdl) {
|
|
||||||
|
|
||||||
case CREATE_DROP :
|
|
||||||
|
|
||||||
// Drop tables first, otherwise a `DROP TYPE ...` will fail as the type is still referenced
|
|
||||||
// by a table.
|
|
||||||
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE)
|
|
||||||
.forEach(e -> tableOps.dropTable(e));
|
|
||||||
|
|
||||||
eachUserTypeInReverseOrder(userTypeOps, e -> userTypeOps.dropUserType(e));
|
|
||||||
|
|
||||||
// FALLTHRU to CREATE case (read: the absence of a `break;` statement here is intentional!)
|
|
||||||
case CREATE :
|
|
||||||
|
|
||||||
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.createUserType(e));
|
|
||||||
|
|
||||||
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE)
|
|
||||||
.forEach(e -> tableOps.createTable(e));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VALIDATE :
|
|
||||||
|
|
||||||
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.validateUserType(getUserType(e), e));
|
|
||||||
|
|
||||||
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE)
|
|
||||||
.forEach(e -> tableOps.validateTable(getTableMetadata(e), e));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UPDATE :
|
|
||||||
|
|
||||||
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e));
|
|
||||||
|
|
||||||
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE)
|
|
||||||
.forEach(e -> tableOps.updateTable(getTableMetadata(e), e));
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyspaceMetadata km = getKeyspaceMetadata();
|
|
||||||
|
|
||||||
for (UserType userType : km.getUserTypes()) {
|
|
||||||
sessionRepository.addUserType(userType.getTypeName(), userType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void eachUserTypeInOrder(UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
|
|
||||||
|
|
||||||
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>();
|
|
||||||
Set<HelenusEntity> stack = new HashSet<HelenusEntity>();
|
|
||||||
|
|
||||||
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.UDT).forEach(e -> {
|
|
||||||
stack.clear();
|
|
||||||
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (session == null) return;
|
||||||
|
|
||||||
|
TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
|
||||||
|
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);
|
||||||
|
|
||||||
|
switch (autoDdl) {
|
||||||
|
case CREATE_DROP:
|
||||||
|
|
||||||
|
// Drop view first, otherwise a `DROP TABLE ...` will fail as the type is still
|
||||||
|
// referenced
|
||||||
|
// by a view.
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.VIEW)
|
||||||
|
.forEach(e -> tableOps.dropView(e));
|
||||||
|
|
||||||
|
// Drop tables second, before DROP TYPE otherwise a `DROP TYPE ...` will fail as
|
||||||
|
// the type is
|
||||||
|
// still referenced by a table.
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.TABLE)
|
||||||
|
.forEach(e -> tableOps.dropTable(e));
|
||||||
|
|
||||||
|
eachUserTypeInReverseOrder(userTypeOps, e -> userTypeOps.dropUserType(e));
|
||||||
|
|
||||||
|
// FALLTHRU to CREATE case (read: the absence of a `break;` statement here is
|
||||||
|
// intentional!)
|
||||||
|
case CREATE:
|
||||||
|
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.createUserType(e));
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.TABLE)
|
||||||
|
.forEach(e -> tableOps.createTable(e));
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.VIEW)
|
||||||
|
.forEach(e -> tableOps.createView(e));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VALIDATE:
|
||||||
|
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.validateUserType(getUserType(e), e));
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.TABLE)
|
||||||
|
.forEach(e -> tableOps.validateTable(getTableMetadata(e), e));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UPDATE:
|
||||||
|
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e));
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.VIEW)
|
||||||
|
.forEach(e -> tableOps.dropView(e));
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.TABLE)
|
||||||
|
.forEach(e -> tableOps.updateTable(getTableMetadata(e), e));
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.VIEW)
|
||||||
|
.forEach(e -> tableOps.createView(e));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void eachUserTypeInReverseOrder(UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
|
KeyspaceMetadata km = getKeyspaceMetadata();
|
||||||
ArrayDeque<HelenusEntity> deque = new ArrayDeque<>();
|
|
||||||
eachUserTypeInOrder(userTypeOps, e -> deque.addFirst(e));
|
|
||||||
deque.stream().forEach(e -> {action.accept(e); });
|
|
||||||
/*
|
|
||||||
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>();
|
|
||||||
Set<HelenusEntity> stack = new HashSet<HelenusEntity>();
|
|
||||||
|
|
||||||
sessionRepository.entities().stream()
|
for (UserType userType : km.getUserTypes()) {
|
||||||
.filter(e -> e.getType() == HelenusEntityType.UDT)
|
sessionRepository.addUserType(userType.getTypeName(), userType);
|
||||||
.collect(Collectors.toCollection(ArrayDeque::new))
|
}
|
||||||
.descendingIterator()
|
}
|
||||||
.forEachRemaining(e -> {
|
|
||||||
stack.clear();
|
private void eachUserTypeInOrder(
|
||||||
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action);
|
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
|
||||||
});
|
|
||||||
*/
|
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>();
|
||||||
|
Set<HelenusEntity> stack = new HashSet<HelenusEntity>();
|
||||||
|
|
||||||
|
sessionRepository
|
||||||
|
.entities()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getType() == HelenusEntityType.UDT)
|
||||||
|
.forEach(
|
||||||
|
e -> {
|
||||||
|
stack.clear();
|
||||||
|
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eachUserTypeInReverseOrder(
|
||||||
|
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
|
||||||
|
ArrayDeque<HelenusEntity> deque = new ArrayDeque<>();
|
||||||
|
eachUserTypeInOrder(userTypeOps, e -> deque.addFirst(e));
|
||||||
|
deque
|
||||||
|
.stream()
|
||||||
|
.forEach(
|
||||||
|
e -> {
|
||||||
|
action.accept(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eachUserTypeInRecursion(
|
||||||
|
HelenusEntity e,
|
||||||
|
Set<HelenusEntity> processedSet,
|
||||||
|
Set<HelenusEntity> stack,
|
||||||
|
UserTypeOperations userTypeOps,
|
||||||
|
Consumer<? super HelenusEntity> action) {
|
||||||
|
|
||||||
|
stack.add(e);
|
||||||
|
|
||||||
|
Collection<HelenusEntity> createBefore = sessionRepository.getUserTypeUses(e);
|
||||||
|
|
||||||
|
for (HelenusEntity be : createBefore) {
|
||||||
|
if (!processedSet.contains(be) && !stack.contains(be)) {
|
||||||
|
eachUserTypeInRecursion(be, processedSet, stack, userTypeOps, action);
|
||||||
|
processedSet.add(be);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void eachUserTypeInRecursion(HelenusEntity e, Set<HelenusEntity> processedSet, Set<HelenusEntity> stack,
|
if (!processedSet.contains(e)) {
|
||||||
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
|
action.accept(e);
|
||||||
|
processedSet.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stack.add(e);
|
private KeyspaceMetadata getKeyspaceMetadata() {
|
||||||
|
if (keyspaceMetadata == null) {
|
||||||
|
keyspaceMetadata =
|
||||||
|
session.getCluster().getMetadata().getKeyspace(usingKeyspace.toLowerCase());
|
||||||
|
}
|
||||||
|
return keyspaceMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
Collection<HelenusEntity> createBefore = sessionRepository.getUserTypeUses(e);
|
private TableMetadata getTableMetadata(HelenusEntity entity) {
|
||||||
|
return getKeyspaceMetadata().getTable(entity.getName().getName());
|
||||||
|
}
|
||||||
|
|
||||||
for (HelenusEntity be : createBefore) {
|
private UserType getUserType(HelenusEntity entity) {
|
||||||
if (!processedSet.contains(be) && !stack.contains(be)) {
|
return getKeyspaceMetadata().getUserType(entity.getName().getName());
|
||||||
eachUserTypeInRecursion(be, processedSet, stack, userTypeOps, action);
|
}
|
||||||
processedSet.add(be);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!processedSet.contains(e)) {
|
|
||||||
action.accept(e);
|
|
||||||
processedSet.add(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyspaceMetadata getKeyspaceMetadata() {
|
|
||||||
if (keyspaceMetadata == null) {
|
|
||||||
keyspaceMetadata = session.getCluster().getMetadata().getKeyspace(usingKeyspace.toLowerCase());
|
|
||||||
}
|
|
||||||
return keyspaceMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TableMetadata getTableMetadata(HelenusEntity entity) {
|
|
||||||
return getKeyspaceMetadata().getTable(entity.getName().getName());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserType getUserType(HelenusEntity entity) {
|
|
||||||
return getKeyspaceMetadata().getUserType(entity.getName().getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,32 +16,30 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.UserType;
|
import com.datastax.driver.core.UserType;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.Collection;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
|
|
||||||
public final class SessionRepository {
|
public final class SessionRepository {
|
||||||
|
|
||||||
private final ImmutableMap<String, UserType> userTypeMap;
|
private final ImmutableMap<String, UserType> userTypeMap;
|
||||||
|
|
||||||
private final ImmutableMap<Class<?>, HelenusEntity> entityMap;
|
private final ImmutableMap<Class<?>, HelenusEntity> entityMap;
|
||||||
|
|
||||||
public SessionRepository(SessionRepositoryBuilder builder) {
|
public SessionRepository(SessionRepositoryBuilder builder) {
|
||||||
|
|
||||||
userTypeMap = ImmutableMap.<String, UserType>builder().putAll(builder.getUserTypeMap()).build();
|
userTypeMap = ImmutableMap.<String, UserType>builder().putAll(builder.getUserTypeMap()).build();
|
||||||
|
|
||||||
entityMap = ImmutableMap.<Class<?>, HelenusEntity>builder().putAll(builder.getEntityMap()).build();
|
entityMap =
|
||||||
}
|
ImmutableMap.<Class<?>, HelenusEntity>builder().putAll(builder.getEntityMap()).build();
|
||||||
|
}
|
||||||
|
|
||||||
public UserType findUserType(String name) {
|
public UserType findUserType(String name) {
|
||||||
return userTypeMap.get(name.toLowerCase());
|
return userTypeMap.get(name.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<HelenusEntity> entities() {
|
|
||||||
return entityMap.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public Collection<HelenusEntity> entities() {
|
||||||
|
return entityMap.values();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,17 +16,15 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.Session;
|
import com.datastax.driver.core.Session;
|
||||||
import com.datastax.driver.core.UDTValue;
|
import com.datastax.driver.core.UDTValue;
|
||||||
import com.datastax.driver.core.UserType;
|
import com.datastax.driver.core.UserType;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
import net.helenus.mapping.HelenusEntityType;
|
import net.helenus.mapping.HelenusEntityType;
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
@ -35,118 +34,112 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class SessionRepositoryBuilder {
|
public final class SessionRepositoryBuilder {
|
||||||
|
|
||||||
private static final Optional<HelenusEntityType> OPTIONAL_UDT = Optional.of(HelenusEntityType.UDT);
|
private static final Optional<HelenusEntityType> OPTIONAL_UDT =
|
||||||
|
Optional.of(HelenusEntityType.UDT);
|
||||||
|
|
||||||
private final Map<Class<?>, HelenusEntity> entityMap = new HashMap<Class<?>, HelenusEntity>();
|
private final Map<Class<?>, HelenusEntity> entityMap = new HashMap<Class<?>, HelenusEntity>();
|
||||||
|
|
||||||
private final Map<String, UserType> userTypeMap = new HashMap<String, UserType>();
|
private final Map<String, UserType> userTypeMap = new HashMap<String, UserType>();
|
||||||
|
|
||||||
private final Multimap<HelenusEntity, HelenusEntity> userTypeUsesMap = HashMultimap.create();
|
private final Multimap<HelenusEntity, HelenusEntity> userTypeUsesMap = HashMultimap.create();
|
||||||
|
|
||||||
private final Session session;
|
private final Session session;
|
||||||
|
|
||||||
|
SessionRepositoryBuilder(Session session) {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
SessionRepositoryBuilder(Session session) {
|
public SessionRepository build() {
|
||||||
this.session = session;
|
return new SessionRepository(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionRepository build() {
|
public Collection<HelenusEntity> getUserTypeUses(HelenusEntity udtName) {
|
||||||
return new SessionRepository(this);
|
return userTypeUsesMap.get(udtName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<HelenusEntity> getUserTypeUses(HelenusEntity udtName) {
|
public Collection<HelenusEntity> entities() {
|
||||||
return userTypeUsesMap.get(udtName);
|
return entityMap.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<HelenusEntity> entities() {
|
protected Map<Class<?>, HelenusEntity> getEntityMap() {
|
||||||
return entityMap.values();
|
return entityMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<Class<?>, HelenusEntity> getEntityMap() {
|
protected Map<String, UserType> getUserTypeMap() {
|
||||||
return entityMap;
|
return userTypeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, UserType> getUserTypeMap() {
|
public void addUserType(String name, UserType userType) {
|
||||||
return userTypeMap;
|
userTypeMap.putIfAbsent(name.toLowerCase(), userType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addUserType(String name, UserType userType) {
|
public HelenusEntity add(Object dsl) {
|
||||||
userTypeMap.putIfAbsent(name.toLowerCase(), userType);
|
return add(dsl, Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusEntity add(Object dsl) {
|
public void addEntity(HelenusEntity entity) {
|
||||||
return add(dsl, Optional.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addEntity(HelenusEntity entity) {
|
HelenusEntity concurrentEntity = entityMap.putIfAbsent(entity.getMappingInterface(), entity);
|
||||||
|
|
||||||
HelenusEntity concurrentEntity = entityMap.putIfAbsent(entity.getMappingInterface(), entity);
|
if (concurrentEntity == null) {
|
||||||
|
addUserDefinedTypes(entity.getOrderedProperties());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (concurrentEntity == null) {
|
public HelenusEntity add(Object dsl, Optional<HelenusEntityType> type) {
|
||||||
addUserDefinedTypes(entity.getOrderedProperties());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
HelenusEntity helenusEntity = Helenus.resolve(dsl, session.getCluster().getMetadata());
|
||||||
|
|
||||||
public HelenusEntity add(Object dsl, Optional<HelenusEntityType> type) {
|
Class<?> iface = helenusEntity.getMappingInterface();
|
||||||
|
|
||||||
HelenusEntity helenusEntity = Helenus.resolve(dsl, session.getCluster().getMetadata());
|
HelenusEntity entity = entityMap.get(iface);
|
||||||
|
|
||||||
Class<?> iface = helenusEntity.getMappingInterface();
|
if (entity == null) {
|
||||||
|
|
||||||
HelenusEntity entity = entityMap.get(iface);
|
entity = helenusEntity;
|
||||||
|
|
||||||
if (entity == null) {
|
if (type.isPresent() && entity.getType() != type.get()) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"unexpected entity type " + entity.getType() + " for " + entity);
|
||||||
|
}
|
||||||
|
|
||||||
entity = helenusEntity;
|
HelenusEntity concurrentEntity = entityMap.putIfAbsent(iface, entity);
|
||||||
|
|
||||||
if (type.isPresent() && entity.getType() != type.get()) {
|
if (concurrentEntity == null) {
|
||||||
throw new HelenusMappingException("unexpected entity type " + entity.getType() + " for " + entity);
|
addUserDefinedTypes(entity.getOrderedProperties());
|
||||||
}
|
} else {
|
||||||
|
entity = concurrentEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HelenusEntity concurrentEntity = entityMap.putIfAbsent(iface, entity);
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
if (concurrentEntity == null) {
|
private void addUserDefinedTypes(Collection<HelenusProperty> props) {
|
||||||
addUserDefinedTypes(entity.getOrderedProperties());
|
|
||||||
} else {
|
|
||||||
entity = concurrentEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
for (HelenusProperty prop : props) {
|
||||||
|
|
||||||
return entity;
|
AbstractDataType type = prop.getDataType();
|
||||||
}
|
|
||||||
|
|
||||||
private void addUserDefinedTypes(Collection<HelenusProperty> props) {
|
if (type instanceof DTDataType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (HelenusProperty prop : props) {
|
if (!UDTValue.class.isAssignableFrom(prop.getJavaType())) {
|
||||||
|
|
||||||
AbstractDataType type = prop.getDataType();
|
for (Class<?> udtClass : type.getTypeArguments()) {
|
||||||
|
|
||||||
if (type instanceof DTDataType) {
|
if (UDTValue.class.isAssignableFrom(udtClass)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UDTValue.class.isAssignableFrom(prop.getJavaType())) {
|
HelenusEntity addedUserType = add(udtClass, OPTIONAL_UDT);
|
||||||
|
|
||||||
for (Class<?> udtClass : type.getTypeArguments()) {
|
|
||||||
|
|
||||||
if (UDTValue.class.isAssignableFrom(udtClass)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
HelenusEntity addedUserType = add(udtClass, OPTIONAL_UDT);
|
|
||||||
|
|
||||||
if (HelenusEntityType.UDT == prop.getEntity().getType()) {
|
|
||||||
userTypeUsesMap.put(prop.getEntity(), addedUserType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (HelenusEntityType.UDT == prop.getEntity().getType()) {
|
||||||
|
userTypeUsesMap.put(prop.getEntity(), addedUserType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,74 +16,92 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.TableMetadata;
|
import com.datastax.driver.core.TableMetadata;
|
||||||
import com.datastax.driver.core.schemabuilder.SchemaStatement;
|
import com.datastax.driver.core.schemabuilder.SchemaStatement;
|
||||||
|
import java.util.List;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
public final class TableOperations {
|
public final class TableOperations {
|
||||||
|
|
||||||
private final AbstractSessionOperations sessionOps;
|
private final AbstractSessionOperations sessionOps;
|
||||||
private final boolean dropUnusedColumns;
|
private final boolean dropUnusedColumns;
|
||||||
private final boolean dropUnusedIndexes;
|
private final boolean dropUnusedIndexes;
|
||||||
|
|
||||||
public TableOperations(AbstractSessionOperations sessionOps, boolean dropUnusedColumns, boolean dropUnusedIndexes) {
|
public TableOperations(
|
||||||
this.sessionOps = sessionOps;
|
AbstractSessionOperations sessionOps, boolean dropUnusedColumns, boolean dropUnusedIndexes) {
|
||||||
this.dropUnusedColumns = dropUnusedColumns;
|
this.sessionOps = sessionOps;
|
||||||
this.dropUnusedIndexes = dropUnusedIndexes;
|
this.dropUnusedColumns = dropUnusedColumns;
|
||||||
}
|
this.dropUnusedIndexes = dropUnusedIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
public void createTable(HelenusEntity entity) {
|
public void createTable(HelenusEntity entity) {
|
||||||
|
sessionOps.execute(SchemaUtil.createTable(entity));
|
||||||
|
executeBatch(SchemaUtil.createIndexes(entity));
|
||||||
|
}
|
||||||
|
|
||||||
sessionOps.execute(SchemaUtil.createTable(entity), true);
|
public void dropTable(HelenusEntity entity) {
|
||||||
|
sessionOps.execute(SchemaUtil.dropTable(entity));
|
||||||
|
}
|
||||||
|
|
||||||
executeBatch(SchemaUtil.createIndexes(entity));
|
public void validateTable(TableMetadata tmd, HelenusEntity entity) {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dropTable(HelenusEntity entity) {
|
|
||||||
|
|
||||||
sessionOps.execute(SchemaUtil.dropTable(entity), true);
|
|
||||||
|
|
||||||
|
if (tmd == null) {
|
||||||
|
throw new HelenusException(
|
||||||
|
"table does not exists "
|
||||||
|
+ entity.getName()
|
||||||
|
+ "for entity "
|
||||||
|
+ entity.getMappingInterface());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateTable(TableMetadata tmd, HelenusEntity entity) {
|
List<SchemaStatement> list = SchemaUtil.alterTable(tmd, entity, dropUnusedColumns);
|
||||||
|
|
||||||
if (tmd == null) {
|
list.addAll(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
|
||||||
throw new HelenusException(
|
|
||||||
"table not exists " + entity.getName() + "for entity " + entity.getMappingInterface());
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SchemaStatement> list = SchemaUtil.alterTable(tmd, entity, dropUnusedColumns);
|
if (!list.isEmpty()) {
|
||||||
|
throw new HelenusException(
|
||||||
|
"schema changed for entity "
|
||||||
|
+ entity.getMappingInterface()
|
||||||
|
+ ", apply this command: "
|
||||||
|
+ list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
list.addAll(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
|
public void updateTable(TableMetadata tmd, HelenusEntity entity) {
|
||||||
|
if (tmd == null) {
|
||||||
|
createTable(entity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!list.isEmpty()) {
|
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
|
||||||
throw new HelenusException(
|
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
|
||||||
"schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + list);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateTable(TableMetadata tmd, HelenusEntity entity) {
|
public void createView(HelenusEntity entity) {
|
||||||
|
sessionOps.execute(
|
||||||
|
SchemaUtil.createMaterializedView(
|
||||||
|
sessionOps.usingKeyspace(), entity.getName().toCql(), entity));
|
||||||
|
// executeBatch(SchemaUtil.createIndexes(entity)); NOTE: Unfortunately C* 3.10 does not yet support 2i on materialized views.
|
||||||
|
}
|
||||||
|
|
||||||
if (tmd == null) {
|
public void dropView(HelenusEntity entity) {
|
||||||
createTable(entity);
|
sessionOps.execute(
|
||||||
return;
|
SchemaUtil.dropMaterializedView(
|
||||||
}
|
sessionOps.usingKeyspace(), entity.getName().toCql(), entity));
|
||||||
|
}
|
||||||
|
|
||||||
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
|
public void updateView(TableMetadata tmd, HelenusEntity entity) {
|
||||||
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
|
if (tmd == null) {
|
||||||
}
|
createTable(entity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private void executeBatch(List<SchemaStatement> list) {
|
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
|
||||||
|
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
|
||||||
|
}
|
||||||
|
|
||||||
list.forEach(s -> {
|
private void executeBatch(List<SchemaStatement> list) {
|
||||||
sessionOps.execute(s, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
list.forEach(s -> sessionOps.execute(s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,816 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
|
import static net.helenus.core.HelenusSession.deleted;
|
||||||
|
|
||||||
|
import com.google.common.base.Stopwatch;
|
||||||
|
import com.google.common.collect.HashBasedTable;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
|
import com.google.common.collect.TreeTraverser;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.cache.Cache;
|
||||||
|
import javax.cache.CacheManager;
|
||||||
|
import javax.cache.configuration.CacheEntryListenerConfiguration;
|
||||||
|
import javax.cache.configuration.Configuration;
|
||||||
|
import javax.cache.integration.CacheLoader;
|
||||||
|
import javax.cache.integration.CacheLoaderException;
|
||||||
|
import javax.cache.integration.CompletionListener;
|
||||||
|
import javax.cache.processor.EntryProcessor;
|
||||||
|
import javax.cache.processor.EntryProcessorException;
|
||||||
|
import javax.cache.processor.EntryProcessorResult;
|
||||||
|
|
||||||
/**
|
import net.helenus.core.cache.CacheUtil;
|
||||||
* Encapsulates the concept of a "transaction" as a unit-of-work.
|
import net.helenus.core.cache.Facet;
|
||||||
*/
|
import net.helenus.core.cache.MapCache;
|
||||||
public class UnitOfWork {
|
import net.helenus.core.operation.AbstractOperation;
|
||||||
|
import net.helenus.core.operation.BatchOperation;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
|
import net.helenus.support.CheckedRunnable;
|
||||||
|
import net.helenus.support.Either;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
private final HelenusSession session;
|
/** Encapsulates the concept of a "transaction" as a unit-of-work. */
|
||||||
private ArrayList<UnitOfWork> nested;
|
public class UnitOfWork implements AutoCloseable {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(UnitOfWork.class);
|
||||||
|
|
||||||
UnitOfWork(HelenusSession session) {
|
public final UnitOfWork parent;
|
||||||
this.session = session;
|
protected final List<UnitOfWork> nested = new ArrayList<>();
|
||||||
// log.record(txn::start)
|
protected final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
|
||||||
}
|
protected final EvictTrackingMapCache<String, Object> statementCache;
|
||||||
|
protected final HelenusSession session;
|
||||||
|
protected String purpose;
|
||||||
|
protected List<String> nestedPurposes = new ArrayList<String>();
|
||||||
|
protected String info;
|
||||||
|
protected int cacheHits = 0;
|
||||||
|
protected int cacheMisses = 0;
|
||||||
|
protected int databaseLookups = 0;
|
||||||
|
protected final Stopwatch elapsedTime;
|
||||||
|
protected Map<String, Double> databaseTime = new HashMap<>();
|
||||||
|
protected double cacheLookupTimeMSecs = 0.0;
|
||||||
|
private List<CheckedRunnable> commitThunks = new ArrayList<>();
|
||||||
|
private List<CheckedRunnable> abortThunks = new ArrayList<>();
|
||||||
|
private Consumer<? super Throwable> exceptionallyThunk;
|
||||||
|
private List<CompletableFuture<?>> asyncOperationFutures = new ArrayList<CompletableFuture<?>>();
|
||||||
|
private boolean aborted = false;
|
||||||
|
private boolean committed = false;
|
||||||
|
private long committedAt = 0L;
|
||||||
|
private BatchOperation batch;
|
||||||
|
|
||||||
/**
|
public UnitOfWork(HelenusSession session) {
|
||||||
* Marks the beginning of a transactional section of work. Will write a record
|
this(session, null);
|
||||||
* to the shared write-ahead log.
|
}
|
||||||
*
|
|
||||||
* @return the handle used to commit or abort the work.
|
|
||||||
*/
|
|
||||||
public UnitOfWork begin() {
|
|
||||||
if (nested == null) {
|
|
||||||
nested = new ArrayList<UnitOfWork>();
|
|
||||||
}
|
|
||||||
UnitOfWork unitOfWork = new UnitOfWork(session);
|
|
||||||
nested.add(unitOfWork);
|
|
||||||
return unitOfWork;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public UnitOfWork(HelenusSession session, UnitOfWork parent) {
|
||||||
* Checks to see if the work performed between calling begin and now can be
|
Objects.requireNonNull(session, "containing session cannot be null");
|
||||||
* committed or not.
|
|
||||||
*
|
|
||||||
* @throws ConflictingUnitOfWorkException
|
|
||||||
* when the work overlaps with other concurrent writers.
|
|
||||||
*/
|
|
||||||
public void commit() throws ConflictingUnitOfWorkException {
|
|
||||||
// nested.foreach.commit()
|
|
||||||
// log.record(txn::provisionalCommit)
|
|
||||||
// examine log for conflicts in read-set and write-set between begin and
|
|
||||||
// provisional commit
|
|
||||||
// if (conflict) { throw new ConflictingUnitOfWorkException(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
this.parent = parent;
|
||||||
* Explicitly discard the work and mark it as as such in the log.
|
if (parent != null) {
|
||||||
*/
|
parent.addNestedUnitOfWork(this);
|
||||||
public void abort() {
|
}
|
||||||
// log.record(txn::abort)
|
this.session = session;
|
||||||
// cache.invalidateSince(txn::start time)
|
CacheLoader<String, Object> cacheLoader = null;
|
||||||
}
|
if (parent != null) {
|
||||||
|
cacheLoader =
|
||||||
|
new CacheLoader<String, Object>() {
|
||||||
|
|
||||||
public String describeConflicts() {
|
Cache<String, Object> cache = parent.getCache();
|
||||||
return "it's complex...";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object load(String key) throws CacheLoaderException {
|
||||||
|
return cache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> loadAll(Iterable<? extends String> keys)
|
||||||
|
throws CacheLoaderException {
|
||||||
|
Map<String, Object> kvp = new HashMap<String, Object>();
|
||||||
|
for (String key : keys) {
|
||||||
|
kvp.put(key, cache.get(key));
|
||||||
|
}
|
||||||
|
return kvp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.elapsedTime = Stopwatch.createUnstarted();
|
||||||
|
this.statementCache = new EvictTrackingMapCache<String, Object>(null, "UOW(" + hashCode() + ")", cacheLoader, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDatabaseTime(String name, Stopwatch amount) {
|
||||||
|
Double time = databaseTime.get(name);
|
||||||
|
if (time == null) {
|
||||||
|
databaseTime.put(name, (double) amount.elapsed(TimeUnit.MICROSECONDS));
|
||||||
|
} else {
|
||||||
|
databaseTime.put(name, time + amount.elapsed(TimeUnit.MICROSECONDS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCacheLookupTime(Stopwatch amount) {
|
||||||
|
cacheLookupTimeMSecs += amount.elapsed(TimeUnit.MICROSECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNestedUnitOfWork(UnitOfWork uow) {
|
||||||
|
synchronized (nested) {
|
||||||
|
nested.add(uow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the beginning of a transactional section of work. Will write a
|
||||||
|
* recordCacheAndDatabaseOperationCount to the shared write-ahead log.
|
||||||
|
*
|
||||||
|
* @return the handle used to commit or abort the work.
|
||||||
|
*/
|
||||||
|
public synchronized UnitOfWork begin() {
|
||||||
|
elapsedTime.start();
|
||||||
|
// log.record(txn::start)
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPurpose() {
|
||||||
|
return purpose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnitOfWork setPurpose(String purpose) {
|
||||||
|
this.purpose = purpose;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFuture(CompletableFuture<?> future) {
|
||||||
|
asyncOperationFutures.add(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfo(String info) {
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordCacheAndDatabaseOperationCount(int cache, int ops) {
|
||||||
|
if (cache > 0) {
|
||||||
|
cacheHits += cache;
|
||||||
|
} else {
|
||||||
|
cacheMisses += Math.abs(cache);
|
||||||
|
}
|
||||||
|
if (ops > 0) {
|
||||||
|
databaseLookups += ops;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String logTimers(String what) {
|
||||||
|
double e = (double) elapsedTime.elapsed(TimeUnit.MICROSECONDS) / 1000.0;
|
||||||
|
double d = 0.0;
|
||||||
|
double c = cacheLookupTimeMSecs / 1000.0;
|
||||||
|
double fc = (c / e) * 100.0;
|
||||||
|
String database = "";
|
||||||
|
if (databaseTime.size() > 0) {
|
||||||
|
List<String> dbt = new ArrayList<>(databaseTime.size());
|
||||||
|
for (Map.Entry<String, Double> dt : databaseTime.entrySet()) {
|
||||||
|
double t = dt.getValue() / 1000.0;
|
||||||
|
d += t;
|
||||||
|
dbt.add(String.format("%s took %,.3fms %,2.2f%%", dt.getKey(), t, (t / e) * 100.0));
|
||||||
|
}
|
||||||
|
double fd = (d / e) * 100.0;
|
||||||
|
database =
|
||||||
|
String.format(
|
||||||
|
", %d quer%s (%,.3fms %,2.2f%% - %s)",
|
||||||
|
databaseLookups, (databaseLookups > 1) ? "ies" : "y", d, fd, String.join(", ", dbt));
|
||||||
|
}
|
||||||
|
String cache = "";
|
||||||
|
if (cacheLookupTimeMSecs > 0) {
|
||||||
|
int cacheLookups = cacheHits + cacheMisses;
|
||||||
|
cache =
|
||||||
|
String.format(
|
||||||
|
" with %d cache lookup%s (%,.3fms %,2.2f%% - %,d hit, %,d miss)",
|
||||||
|
cacheLookups, cacheLookups > 1 ? "s" : "", c, fc, cacheHits, cacheMisses);
|
||||||
|
}
|
||||||
|
String da = "";
|
||||||
|
if (databaseTime.size() > 0 || cacheLookupTimeMSecs > 0) {
|
||||||
|
double dat = d + c;
|
||||||
|
double daf = (dat / e) * 100;
|
||||||
|
da =
|
||||||
|
String.format(
|
||||||
|
" consuming %,.3fms for data access, or %,2.2f%% of total UOW time.", dat, daf);
|
||||||
|
}
|
||||||
|
String x = nestedPurposes.stream().distinct().collect(Collectors.joining(", "));
|
||||||
|
String n =
|
||||||
|
nested
|
||||||
|
.stream()
|
||||||
|
.map(uow -> String.valueOf(uow.hashCode()))
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
String s =
|
||||||
|
String.format(
|
||||||
|
Locale.US,
|
||||||
|
"UOW(%s%s) %s in %,.3fms%s%s%s%s%s%s",
|
||||||
|
hashCode(),
|
||||||
|
(nested.size() > 0 ? ", [" + n + "]" : ""),
|
||||||
|
what,
|
||||||
|
e,
|
||||||
|
cache,
|
||||||
|
database,
|
||||||
|
da,
|
||||||
|
(purpose == null ? "" : " " + purpose),
|
||||||
|
(nestedPurposes.isEmpty()) ? "" : ", " + x,
|
||||||
|
(info == null) ? "" : " " + info);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyPostCommitFunctions(String what, List<CheckedRunnable> thunks, Consumer<? super Throwable> exceptionallyThunk) {
|
||||||
|
if (!thunks.isEmpty()) {
|
||||||
|
for (CheckedRunnable f : thunks) {
|
||||||
|
try {
|
||||||
|
f.run();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
if (exceptionallyThunk != null) {
|
||||||
|
exceptionallyThunk.accept(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Object> cacheLookup(List<Facet> facets) {
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
Optional<Object> result = Optional.empty();
|
||||||
|
for (Facet facet : facets) {
|
||||||
|
if (!facet.fixed()) {
|
||||||
|
String columnName = facet.name() + "==" + facet.value();
|
||||||
|
Either<Object, List<Facet>> eitherValue = cache.get(tableName, columnName);
|
||||||
|
if (eitherValue != null) {
|
||||||
|
Object value = deleted;
|
||||||
|
if (eitherValue.isLeft()) {
|
||||||
|
value = eitherValue.getLeft();
|
||||||
|
}
|
||||||
|
return Optional.of(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Be sure to check all enclosing UnitOfWork caches as well, we may be nested.
|
||||||
|
result = checkParentCache(facets);
|
||||||
|
if (result.isPresent()) {
|
||||||
|
Object r = result.get();
|
||||||
|
Class<?> iface = MappingUtil.getMappingInterface(r);
|
||||||
|
if (Helenus.entity(iface).isDraftable()) {
|
||||||
|
cacheUpdate(r, facets);
|
||||||
|
} else {
|
||||||
|
cacheUpdate(SerializationUtils.<Serializable>clone((Serializable) r), facets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Object> checkParentCache(List<Facet> facets) {
|
||||||
|
Optional<Object> result = Optional.empty();
|
||||||
|
if (parent != null) {
|
||||||
|
result = parent.checkParentCache(facets);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Facet> cacheEvict(List<Facet> facets) {
|
||||||
|
Either<Object, List<Facet>> deletedObjectFacets = Either.right(facets);
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
Optional<Object> optionalValue = cacheLookup(facets);
|
||||||
|
|
||||||
|
for (Facet facet : facets) {
|
||||||
|
if (!facet.fixed()) {
|
||||||
|
String columnKey = facet.name() + "==" + facet.value();
|
||||||
|
// mark the value identified by the facet to `deleted`
|
||||||
|
cache.put(tableName, columnKey, deletedObjectFacets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, look for other row/col pairs that referenced the same object, mark them
|
||||||
|
// `deleted` if the cache had a value before we added the deleted marker objects.
|
||||||
|
if (optionalValue.isPresent()) {
|
||||||
|
Object value = optionalValue.get();
|
||||||
|
cache
|
||||||
|
.columnKeySet()
|
||||||
|
.forEach(
|
||||||
|
columnKey -> {
|
||||||
|
Either<Object, List<Facet>> eitherCachedValue = cache.get(tableName, columnKey);
|
||||||
|
if (eitherCachedValue.isLeft()) {
|
||||||
|
Object cachedValue = eitherCachedValue.getLeft();
|
||||||
|
if (cachedValue == value) {
|
||||||
|
cache.put(tableName, columnKey, deletedObjectFacets);
|
||||||
|
String[] parts = columnKey.split("==");
|
||||||
|
facets.add(new Facet<String>(parts[0], parts[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return facets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cache<String, Object> getCache() {
|
||||||
|
return statementCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object cacheUpdate(Object value, List<Facet> facets) {
|
||||||
|
Object result = null;
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
for (Facet facet : facets) {
|
||||||
|
if (!facet.fixed()) {
|
||||||
|
if (facet.alone()) {
|
||||||
|
String columnName = facet.name() + "==" + facet.value();
|
||||||
|
if (result == null) result = cache.get(tableName, columnName);
|
||||||
|
cache.put(tableName, columnName, Either.left(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void batch(AbstractOperation s) {
|
||||||
|
if (batch == null) {
|
||||||
|
batch = new BatchOperation(session);
|
||||||
|
}
|
||||||
|
batch.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterator<UnitOfWork> getChildNodes() {
|
||||||
|
return nested.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if the work performed between calling begin and now can be committed or not.
|
||||||
|
*
|
||||||
|
* @return a function from which to chain work that only happens when commit is successful
|
||||||
|
* @throws HelenusException when the work overlaps with other concurrent writers.
|
||||||
|
*/
|
||||||
|
public synchronized PostCommitFunction<Void, Void> commit() throws HelenusException {
|
||||||
|
|
||||||
|
if (isDone()) {
|
||||||
|
return PostCommitFunction.NULL_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the outer-most UOW batches statements for commit time, execute them.
|
||||||
|
if (batch != null) {
|
||||||
|
committedAt = batch.sync(this); //TODO(gburd): update cache with writeTime...
|
||||||
|
}
|
||||||
|
|
||||||
|
// All nested UnitOfWork should be committed (not aborted) before calls to
|
||||||
|
// commit, check.
|
||||||
|
boolean canCommit = true;
|
||||||
|
TreeTraverser<UnitOfWork> traverser = TreeTraverser.using(node -> node::getChildNodes);
|
||||||
|
for (UnitOfWork uow : traverser.postOrderTraversal(this)) {
|
||||||
|
if (this != uow) {
|
||||||
|
canCommit &= (!uow.aborted && uow.committed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canCommit) {
|
||||||
|
|
||||||
|
if (parent == null) {
|
||||||
|
|
||||||
|
// Apply all post-commit abort functions, this is the outer-most UnitOfWork.
|
||||||
|
traverser
|
||||||
|
.postOrderTraversal(this)
|
||||||
|
.forEach(
|
||||||
|
uow -> {
|
||||||
|
applyPostCommitFunctions("aborted", abortThunks, exceptionallyThunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
elapsedTime.stop();
|
||||||
|
if (LOG.isInfoEnabled()) {
|
||||||
|
LOG.info(logTimers("aborted"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PostCommitFunction.NULL_ABORT;
|
||||||
|
} else {
|
||||||
|
committed = true;
|
||||||
|
aborted = false;
|
||||||
|
|
||||||
|
if (parent == null) {
|
||||||
|
|
||||||
|
// Apply all post-commit commit functions, this is the outer-most UnitOfWork.
|
||||||
|
traverser
|
||||||
|
.postOrderTraversal(this)
|
||||||
|
.forEach(
|
||||||
|
uow -> {
|
||||||
|
applyPostCommitFunctions("committed", uow.commitThunks, exceptionallyThunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge our statement cache into the session cache if it exists.
|
||||||
|
CacheManager cacheManager = session.getCacheManager();
|
||||||
|
if (cacheManager != null) {
|
||||||
|
for (Map.Entry<String, Object> entry :
|
||||||
|
(Set<Map.Entry<String, Object>>) statementCache.<Map>unwrap(Map.class).entrySet()) {
|
||||||
|
String[] keyParts = entry.getKey().split("\\.");
|
||||||
|
if (keyParts.length == 2) {
|
||||||
|
String cacheName = keyParts[0];
|
||||||
|
String key = keyParts[1];
|
||||||
|
if (!StringUtils.isBlank(cacheName) && !StringUtils.isBlank(key)) {
|
||||||
|
Cache<Object, Object> cache = cacheManager.getCache(cacheName);
|
||||||
|
if (cache != null) {
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (value == deleted) {
|
||||||
|
cache.remove(key);
|
||||||
|
} else {
|
||||||
|
cache.put(key.toString(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge our cache into the session cache.
|
||||||
|
session.mergeCache(cache);
|
||||||
|
|
||||||
|
// Spoil any lingering futures that may be out there.
|
||||||
|
asyncOperationFutures.forEach(
|
||||||
|
f ->
|
||||||
|
f.completeExceptionally(
|
||||||
|
new HelenusException(
|
||||||
|
"Futures must be resolved before their unit of work has committed/aborted.")));
|
||||||
|
|
||||||
|
elapsedTime.stop();
|
||||||
|
if (LOG.isInfoEnabled()) {
|
||||||
|
LOG.info(logTimers("committed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PostCommitFunction.NULL_COMMIT;
|
||||||
|
} else {
|
||||||
|
// Merge cache and statistics into parent if there is one.
|
||||||
|
parent.statementCache.putAll(statementCache.<Map>unwrap(Map.class));
|
||||||
|
parent.statementCache.removeAll(statementCache.getDeletions());
|
||||||
|
parent.mergeCache(cache);
|
||||||
|
parent.addBatched(batch);
|
||||||
|
if (purpose != null) {
|
||||||
|
parent.nestedPurposes.add(purpose);
|
||||||
|
}
|
||||||
|
parent.cacheHits += cacheHits;
|
||||||
|
parent.cacheMisses += cacheMisses;
|
||||||
|
parent.databaseLookups += databaseLookups;
|
||||||
|
parent.cacheLookupTimeMSecs += cacheLookupTimeMSecs;
|
||||||
|
for (Map.Entry<String, Double> dt : databaseTime.entrySet()) {
|
||||||
|
String name = dt.getKey();
|
||||||
|
if (parent.databaseTime.containsKey(name)) {
|
||||||
|
double t = parent.databaseTime.get(name);
|
||||||
|
parent.databaseTime.put(name, t + dt.getValue());
|
||||||
|
} else {
|
||||||
|
parent.databaseTime.put(name, dt.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(gburd): hopefully we'll be able to detect conflicts here and so we'd want to...
|
||||||
|
// else {
|
||||||
|
// Constructor<T> ctor = clazz.getConstructor(conflictExceptionClass);
|
||||||
|
// T object = ctor.newInstance(new Object[] { String message });
|
||||||
|
// }
|
||||||
|
return new PostCommitFunction<Void, Void>(commitThunks, abortThunks, exceptionallyThunk, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBatched(BatchOperation batchArg) {
|
||||||
|
if (batchArg != null) {
|
||||||
|
if (this.batch == null) {
|
||||||
|
this.batch = batchArg;
|
||||||
|
} else {
|
||||||
|
this.batch.addAll(batchArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly abort the work within this unit of work. Any nested aborted unit of work will
|
||||||
|
* trigger the entire unit of work to commit.
|
||||||
|
*/
|
||||||
|
public synchronized void abort() {
|
||||||
|
if (!aborted) {
|
||||||
|
aborted = true;
|
||||||
|
|
||||||
|
// Spoil any pending futures created within the context of this unit of work.
|
||||||
|
asyncOperationFutures.forEach(
|
||||||
|
f ->
|
||||||
|
f.completeExceptionally(
|
||||||
|
new HelenusException(
|
||||||
|
"Futures must be resolved before their unit of work has committed/aborted.")));
|
||||||
|
|
||||||
|
TreeTraverser<UnitOfWork> traverser = TreeTraverser.using(node -> node::getChildNodes);
|
||||||
|
traverser
|
||||||
|
.postOrderTraversal(this)
|
||||||
|
.forEach(
|
||||||
|
uow -> {
|
||||||
|
applyPostCommitFunctions("aborted", uow.abortThunks, exceptionallyThunk);
|
||||||
|
uow.abortThunks.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parent == null) {
|
||||||
|
if (elapsedTime.isRunning()) {
|
||||||
|
elapsedTime.stop();
|
||||||
|
}
|
||||||
|
if (LOG.isInfoEnabled()) {
|
||||||
|
LOG.info(logTimers("aborted"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gburd): when we integrate the transaction support we'll need to...
|
||||||
|
// log.record(txn::abort)
|
||||||
|
// cache.invalidateSince(txn::start time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeCache(Table<String, String, Either<Object, List<Facet>>> from) {
|
||||||
|
Table<String, String, Either<Object, List<Facet>>> to = this.cache;
|
||||||
|
from.rowMap()
|
||||||
|
.forEach(
|
||||||
|
(rowKey, columnMap) -> {
|
||||||
|
columnMap.forEach(
|
||||||
|
(columnKey, value) -> {
|
||||||
|
if (to.contains(rowKey, columnKey)) {
|
||||||
|
to.put(
|
||||||
|
rowKey,
|
||||||
|
columnKey,
|
||||||
|
Either.left(
|
||||||
|
CacheUtil.merge(
|
||||||
|
to.get(rowKey, columnKey).getLeft(),
|
||||||
|
from.get(rowKey, columnKey).getLeft())));
|
||||||
|
} else {
|
||||||
|
to.put(rowKey, columnKey, from.get(rowKey, columnKey));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDone() {
|
||||||
|
return aborted || committed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String describeConflicts() {
|
||||||
|
return "it's complex...";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws HelenusException {
|
||||||
|
// Closing a UnitOfWork will abort iff we've not already aborted or committed this unit of work.
|
||||||
|
if (aborted == false && committed == false) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAborted() {
|
||||||
|
return aborted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCommitted() {
|
||||||
|
return committed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long committedAt() {
|
||||||
|
return committedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EvictTrackingMapCache<K, V> implements Cache<K, V> {
|
||||||
|
private final Set<K> deletes;
|
||||||
|
private final Cache<K, V> delegate;
|
||||||
|
|
||||||
|
public EvictTrackingMapCache(CacheManager manager, String name, CacheLoader<K, V> cacheLoader,
|
||||||
|
boolean isReadThrough) {
|
||||||
|
deletes = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
delegate = new MapCache<>(manager, name, cacheLoader, isReadThrough);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Non-interface method; should only be called by UnitOfWork when merging to an enclosing UnitOfWork. */
|
||||||
|
public Set<K> getDeletions() {
|
||||||
|
return new HashSet<>(deletes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(K key) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<K, V> getAll(Set<? extends K> keys) {
|
||||||
|
Set<? extends K> clonedKeys = new HashSet<>(keys);
|
||||||
|
clonedKeys.removeAll(deletes);
|
||||||
|
return delegate.getAll(clonedKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(K key) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) {
|
||||||
|
Set<? extends K> clonedKeys = new HashSet<>(keys);
|
||||||
|
clonedKeys.removeAll(deletes);
|
||||||
|
delegate.loadAll(clonedKeys, replaceExistingValues, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(K key, V value) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
deletes.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getAndPut(K key, V value) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
deletes.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.getAndPut(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> map) {
|
||||||
|
deletes.removeAll(map.keySet());
|
||||||
|
delegate.putAll(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean putIfAbsent(K key, V value) {
|
||||||
|
if (!delegate.containsKey(key) && deletes.contains(key)) {
|
||||||
|
deletes.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.putIfAbsent(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(K key) {
|
||||||
|
boolean removed = delegate.remove(key);
|
||||||
|
deletes.add(key);
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(K key, V value) {
|
||||||
|
boolean removed = delegate.remove(key, value);
|
||||||
|
if (removed) {
|
||||||
|
deletes.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getAndRemove(K key) {
|
||||||
|
V value = delegate.getAndRemove(key);
|
||||||
|
deletes.add(key);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll(Set<? extends K> keys) {
|
||||||
|
Set<? extends K> cloneKeys = new HashSet<>(keys);
|
||||||
|
delegate.removeAll(cloneKeys);
|
||||||
|
deletes.addAll(cloneKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public synchronized void removeAll() {
|
||||||
|
Map<K, V> impl = delegate.unwrap(Map.class);
|
||||||
|
Set<K> keys = impl.keySet();
|
||||||
|
delegate.removeAll();
|
||||||
|
deletes.addAll(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
delegate.clear();
|
||||||
|
// TODO(gburd): all parents too
|
||||||
|
deletes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replace(K key, V oldValue, V newValue) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.replace(key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replace(K key, V value) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.replace(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getAndReplace(K key, V value) {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.getAndReplace(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <C extends Configuration<K, V>> C getConfiguration(Class<C> clazz) {
|
||||||
|
return delegate.getConfiguration(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T invoke(K key, EntryProcessor<K, V, T> processor, Object... arguments)
|
||||||
|
throws EntryProcessorException {
|
||||||
|
if (deletes.contains(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.invoke(key, processor, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> processor,
|
||||||
|
Object... arguments) {
|
||||||
|
Set<? extends K> clonedKeys = new HashSet<>(keys);
|
||||||
|
clonedKeys.removeAll(deletes);
|
||||||
|
return delegate.invokeAll(clonedKeys, processor, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return delegate.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CacheManager getCacheManager() {
|
||||||
|
return delegate.getCacheManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
delegate.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return delegate.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> clazz) {
|
||||||
|
return delegate.unwrap(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCacheEntryListener(CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
|
||||||
|
delegate.registerCacheEntryListener(cacheEntryListenerConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deregisterCacheEntryListener(CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
|
||||||
|
delegate.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<K, V>> iterator() {
|
||||||
|
return delegate.iterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,69 +16,62 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core;
|
package net.helenus.core;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.UserType;
|
import com.datastax.driver.core.UserType;
|
||||||
import com.datastax.driver.core.schemabuilder.SchemaStatement;
|
import com.datastax.driver.core.schemabuilder.SchemaStatement;
|
||||||
|
import java.util.List;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
public final class UserTypeOperations {
|
public final class UserTypeOperations {
|
||||||
|
|
||||||
private final AbstractSessionOperations sessionOps;
|
private final AbstractSessionOperations sessionOps;
|
||||||
private final boolean dropUnusedColumns;
|
private final boolean dropUnusedColumns;
|
||||||
|
|
||||||
public UserTypeOperations(AbstractSessionOperations sessionOps, boolean dropUnusedColumns) {
|
public UserTypeOperations(AbstractSessionOperations sessionOps, boolean dropUnusedColumns) {
|
||||||
this.sessionOps = sessionOps;
|
this.sessionOps = sessionOps;
|
||||||
this.dropUnusedColumns = dropUnusedColumns;
|
this.dropUnusedColumns = dropUnusedColumns;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createUserType(HelenusEntity entity) {
|
public void createUserType(HelenusEntity entity) {
|
||||||
|
|
||||||
sessionOps.execute(SchemaUtil.createUserType(entity), true);
|
sessionOps.execute(SchemaUtil.createUserType(entity));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
public void dropUserType(HelenusEntity entity) {
|
||||||
|
|
||||||
public void dropUserType(HelenusEntity entity) {
|
sessionOps.execute(SchemaUtil.dropUserType(entity));
|
||||||
|
}
|
||||||
|
|
||||||
sessionOps.execute(SchemaUtil.dropUserType(entity), true);
|
public void validateUserType(UserType userType, HelenusEntity entity) {
|
||||||
|
|
||||||
|
if (userType == null) {
|
||||||
|
throw new HelenusException(
|
||||||
|
"userType not exists " + entity.getName() + "for entity " + entity.getMappingInterface());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateUserType(UserType userType, HelenusEntity entity) {
|
List<SchemaStatement> list = SchemaUtil.alterUserType(userType, entity, dropUnusedColumns);
|
||||||
|
|
||||||
if (userType == null) {
|
if (!list.isEmpty()) {
|
||||||
throw new HelenusException(
|
throw new HelenusException(
|
||||||
"userType not exists " + entity.getName() + "for entity " + entity.getMappingInterface());
|
"schema changed for entity "
|
||||||
}
|
+ entity.getMappingInterface()
|
||||||
|
+ ", apply this command: "
|
||||||
|
+ list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<SchemaStatement> list = SchemaUtil.alterUserType(userType, entity, dropUnusedColumns);
|
public void updateUserType(UserType userType, HelenusEntity entity) {
|
||||||
|
|
||||||
if (!list.isEmpty()) {
|
if (userType == null) {
|
||||||
throw new HelenusException(
|
createUserType(entity);
|
||||||
"schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + list);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
executeBatch(SchemaUtil.alterUserType(userType, entity, dropUnusedColumns));
|
||||||
|
}
|
||||||
|
|
||||||
public void updateUserType(UserType userType, HelenusEntity entity) {
|
private void executeBatch(List<SchemaStatement> list) {
|
||||||
|
|
||||||
if (userType == null) {
|
|
||||||
createUserType(entity);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
executeBatch(SchemaUtil.alterUserType(userType, entity, dropUnusedColumns));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeBatch(List<SchemaStatement> list) {
|
|
||||||
|
|
||||||
list.forEach(s -> {
|
|
||||||
sessionOps.execute(s, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
list.forEach(s -> sessionOps.execute(s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.annotation;
|
package net.helenus.core.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
|
@ -7,5 +23,4 @@ import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
public @interface Cacheable {
|
public @interface Cacheable {}
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.annotation;
|
package net.helenus.core.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import net.helenus.core.ConflictingUnitOfWorkException;
|
import net.helenus.core.ConflictingUnitOfWorkException;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Retry {
|
public @interface Retry {
|
||||||
|
|
||||||
Class<? extends Exception>[] on() default ConflictingUnitOfWorkException.class;
|
Class<? extends Exception>[] on() default {
|
||||||
|
ConflictingUnitOfWorkException.class, TimeoutException.class
|
||||||
|
};
|
||||||
|
|
||||||
int times() default 3;
|
int times() default 3;
|
||||||
}
|
}
|
||||||
|
|
98
src/main/java/net/helenus/core/aspect/RetryAspect.java
Normal file
98
src/main/java/net/helenus/core/aspect/RetryAspect.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.aspect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import net.helenus.core.annotation.Retry;
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
@Aspect
|
||||||
|
public class RetryAspect {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RetryAspect.class);
|
||||||
|
|
||||||
|
@Around("@annotation(net.helenus.core.annotations.Retry)")
|
||||||
|
public Object retry(ProceedingJoinPoint pjp) throws Throwable {
|
||||||
|
Retry retryAnnotation = getRetryAnnotation(pjp);
|
||||||
|
return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object proceed(ProceedingJoinPoint pjp) throws Throwable {
|
||||||
|
return pjp.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable {
|
||||||
|
int times = retryAnnotation.times();
|
||||||
|
Class<? extends Throwable>[] retryOn = retryAnnotation.on();
|
||||||
|
Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!");
|
||||||
|
Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!");
|
||||||
|
log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn));
|
||||||
|
return tryProceeding(pjp, times, retryOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object tryProceeding(
|
||||||
|
ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn) throws Throwable {
|
||||||
|
try {
|
||||||
|
return proceed(pjp);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
if (isRetryThrowable(throwable, retryOn) && times-- > 0) {
|
||||||
|
log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn));
|
||||||
|
return tryProceeding(pjp, times, retryOn);
|
||||||
|
}
|
||||||
|
throw throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) {
|
||||||
|
Throwable[] causes = ExceptionUtils.getThrowables(throwable);
|
||||||
|
for (Throwable cause : causes) {
|
||||||
|
for (Class<? extends Throwable> retryThrowable : retryOn) {
|
||||||
|
if (retryThrowable.isAssignableFrom(cause.getClass())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException {
|
||||||
|
MethodSignature signature = (MethodSignature) pjp.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class);
|
||||||
|
|
||||||
|
if (retryAnnotation != null) {
|
||||||
|
return retryAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?>[] argClasses = new Class[pjp.getArgs().length];
|
||||||
|
for (int i = 0; i < pjp.getArgs().length; i++) {
|
||||||
|
argClasses[i] = pjp.getArgs()[i].getClass();
|
||||||
|
}
|
||||||
|
method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses);
|
||||||
|
return AnnotationUtils.findAnnotation(method, Retry.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package net.helenus.core.aspect;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import net.helenus.core.annotation.Retry;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
@ -13,71 +13,69 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import net.helenus.core.annotation.Retry;
|
|
||||||
|
|
||||||
@Aspect
|
@Aspect
|
||||||
public class RetryConcurrentUnitOfWorkAspect {
|
public class RetryConcurrentUnitOfWorkAspect {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(RetryConcurrentUnitOfWorkAspect.class);
|
private static final Logger log = LoggerFactory.getLogger(RetryConcurrentUnitOfWorkAspect.class);
|
||||||
|
|
||||||
@Around("@annotation(net.helenus.core.annotations.Retry)")
|
@Around("@annotation(net.helenus.core.annotations.Retry)")
|
||||||
public Object retry(ProceedingJoinPoint pjp) throws Throwable {
|
public Object retry(ProceedingJoinPoint pjp) throws Throwable {
|
||||||
Retry retryAnnotation = getRetryAnnotation(pjp);
|
Retry retryAnnotation = getRetryAnnotation(pjp);
|
||||||
return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp);
|
return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object proceed(ProceedingJoinPoint pjp) throws Throwable {
|
private Object proceed(ProceedingJoinPoint pjp) throws Throwable {
|
||||||
return pjp.proceed();
|
return pjp.proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable {
|
private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable {
|
||||||
int times = retryAnnotation.times();
|
int times = retryAnnotation.times();
|
||||||
Class<? extends Throwable>[] retryOn = retryAnnotation.on();
|
Class<? extends Throwable>[] retryOn = retryAnnotation.on();
|
||||||
Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!");
|
Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!");
|
||||||
Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!");
|
Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!");
|
||||||
log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn));
|
log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn));
|
||||||
return tryProceeding(pjp, times, retryOn);
|
return tryProceeding(pjp, times, retryOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object tryProceeding(ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn)
|
private Object tryProceeding(
|
||||||
throws Throwable {
|
ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn) throws Throwable {
|
||||||
try {
|
try {
|
||||||
return proceed(pjp);
|
return proceed(pjp);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
if (isRetryThrowable(throwable, retryOn) && times-- > 0) {
|
if (isRetryThrowable(throwable, retryOn) && times-- > 0) {
|
||||||
log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn));
|
log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn));
|
||||||
return tryProceeding(pjp, times, retryOn);
|
return tryProceeding(pjp, times, retryOn);
|
||||||
}
|
}
|
||||||
throw throwable;
|
throw throwable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) {
|
private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) {
|
||||||
Throwable[] causes = ExceptionUtils.getThrowables(throwable);
|
Throwable[] causes = ExceptionUtils.getThrowables(throwable);
|
||||||
for (Throwable cause : causes) {
|
for (Throwable cause : causes) {
|
||||||
for (Class<? extends Throwable> retryThrowable : retryOn) {
|
for (Class<? extends Throwable> retryThrowable : retryOn) {
|
||||||
if (retryThrowable.isAssignableFrom(cause.getClass())) {
|
if (retryThrowable.isAssignableFrom(cause.getClass())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException {
|
private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException {
|
||||||
MethodSignature signature = (MethodSignature) pjp.getSignature();
|
MethodSignature signature = (MethodSignature) pjp.getSignature();
|
||||||
Method method = signature.getMethod();
|
Method method = signature.getMethod();
|
||||||
Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class);
|
Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class);
|
||||||
|
|
||||||
if (retryAnnotation != null) {
|
if (retryAnnotation != null) {
|
||||||
return retryAnnotation;
|
return retryAnnotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
Class[] argClasses = new Class[pjp.getArgs().length];
|
Class[] argClasses = new Class[pjp.getArgs().length];
|
||||||
for (int i = 0; i < pjp.getArgs().length; i++) {
|
for (int i = 0; i < pjp.getArgs().length; i++) {
|
||||||
argClasses[i] = pjp.getArgs()[i].getClass();
|
argClasses[i] = pjp.getArgs()[i].getClass();
|
||||||
}
|
}
|
||||||
method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses);
|
method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses);
|
||||||
return AnnotationUtils.findAnnotation(method, Retry.class);
|
return AnnotationUtils.findAnnotation(method, Retry.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
60
src/main/java/net/helenus/core/cache/BoundFacet.java
vendored
Normal file
60
src/main/java/net/helenus/core/cache/BoundFacet.java
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.cache;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
|
public class BoundFacet extends Facet<String> {
|
||||||
|
private final Map<HelenusProperty, Object> properties;
|
||||||
|
|
||||||
|
public BoundFacet(HelenusProperty property, Object value) {
|
||||||
|
super(property.getPropertyName(), value == null ? null : value.toString());
|
||||||
|
this.properties = new HashMap<HelenusProperty, Object>(1);
|
||||||
|
this.properties.put(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<HelenusProperty> getProperties() {
|
||||||
|
return properties.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundFacet(String name, Map<HelenusProperty, Object> properties) {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
(properties.keySet().size() > 1)
|
||||||
|
? "["
|
||||||
|
+ String.join(
|
||||||
|
", ",
|
||||||
|
properties
|
||||||
|
.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(key -> properties.get(key).toString())
|
||||||
|
.collect(Collectors.toSet()))
|
||||||
|
+ "]"
|
||||||
|
: String.join(
|
||||||
|
"",
|
||||||
|
properties
|
||||||
|
.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(key -> properties.get(key).toString())
|
||||||
|
.collect(Collectors.toSet())));
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
}
|
221
src/main/java/net/helenus/core/cache/CacheUtil.java
vendored
Normal file
221
src/main/java/net/helenus/core/cache/CacheUtil.java
vendored
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
package net.helenus.core.cache;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import net.helenus.core.Helenus;
|
||||||
|
import net.helenus.core.reflect.Entity;
|
||||||
|
import net.helenus.core.reflect.MapExportable;
|
||||||
|
import net.helenus.mapping.HelenusEntity;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
|
import net.helenus.mapping.value.BeanColumnValueProvider;
|
||||||
|
|
||||||
|
public class CacheUtil {
|
||||||
|
|
||||||
|
public static List<String[]> combinations(List<String> items) {
|
||||||
|
int n = items.size();
|
||||||
|
if (n > 20) throw new IllegalArgumentException(n + " is out of range");
|
||||||
|
long e = Math.round(Math.pow(2, n));
|
||||||
|
List<String[]> out = new ArrayList<String[]>((int) e - 1);
|
||||||
|
for (int k = 1; k <= items.size(); k++) {
|
||||||
|
kCombinations(items, 0, k, new String[k], out);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void kCombinations(
|
||||||
|
List<String> items, int n, int k, String[] arr, List<String[]> out) {
|
||||||
|
if (k == 0) {
|
||||||
|
out.add(arr.clone());
|
||||||
|
} else {
|
||||||
|
for (int i = n; i <= items.size() - k; i++) {
|
||||||
|
arr[arr.length - k] = items.get(i);
|
||||||
|
kCombinations(items, i + 1, k - 1, arr, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> flatKeys(String table, List<Facet> facets) {
|
||||||
|
return flattenFacets(facets)
|
||||||
|
.stream()
|
||||||
|
.map(
|
||||||
|
combination -> {
|
||||||
|
return table + "." + Arrays.toString(combination);
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String[]> flattenFacets(List<Facet> facets) {
|
||||||
|
List<String[]> combinations =
|
||||||
|
CacheUtil.combinations(
|
||||||
|
facets
|
||||||
|
.stream()
|
||||||
|
.filter(facet -> !facet.fixed())
|
||||||
|
.filter(facet -> facet.value() != null)
|
||||||
|
.map(
|
||||||
|
facet -> {
|
||||||
|
return facet.name() + "==" + facet.value();
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
// TODO(gburd): rework so as to not generate the combinations at all rather than filter
|
||||||
|
facets =
|
||||||
|
facets
|
||||||
|
.stream()
|
||||||
|
.filter(f -> !f.fixed())
|
||||||
|
.filter(f -> !f.alone() || !f.combined())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (Facet facet : facets) {
|
||||||
|
combinations =
|
||||||
|
combinations
|
||||||
|
.stream()
|
||||||
|
.filter(
|
||||||
|
combo -> {
|
||||||
|
// When used alone, this facet is not distinct so don't use it as a key.
|
||||||
|
if (combo.length == 1) {
|
||||||
|
if (!facet.alone() && combo[0].startsWith(facet.name() + "==")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!facet.combined()) {
|
||||||
|
for (String c : combo) {
|
||||||
|
// Don't use this facet in combination with others to create keys.
|
||||||
|
if (c.startsWith(facet.name() + "==")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
return combinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Merge changed values in the map behind `from` into `to`. */
|
||||||
|
public static Object merge(Object t, Object f) {
|
||||||
|
HelenusEntity entity = Helenus.resolve(MappingUtil.getMappingInterface(t));
|
||||||
|
|
||||||
|
if (t == f) return t;
|
||||||
|
if (f == null) return t;
|
||||||
|
if (t == null) return f;
|
||||||
|
|
||||||
|
if (t instanceof MapExportable
|
||||||
|
&& t instanceof Entity
|
||||||
|
&& f instanceof MapExportable
|
||||||
|
&& f instanceof Entity) {
|
||||||
|
Entity to = (Entity) t;
|
||||||
|
Entity from = (Entity) f;
|
||||||
|
Map<String, Object> toValueMap = ((MapExportable) to).toMap();
|
||||||
|
Map<String, Object> fromValueMap = ((MapExportable) from).toMap();
|
||||||
|
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
||||||
|
switch (prop.getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
Object toVal = BeanColumnValueProvider.INSTANCE.getColumnValue(to, -1, prop, false);
|
||||||
|
Object fromVal = BeanColumnValueProvider.INSTANCE.getColumnValue(from, -1, prop, false);
|
||||||
|
String ttlKey = ttlKey(prop);
|
||||||
|
String writeTimeKey = writeTimeKey(prop);
|
||||||
|
int[] toTtlI = (int[]) toValueMap.get(ttlKey);
|
||||||
|
int toTtl = (toTtlI != null) ? toTtlI[0] : 0;
|
||||||
|
Long toWriteTime = (Long) toValueMap.get(writeTimeKey);
|
||||||
|
int[] fromTtlI = (int[]) fromValueMap.get(ttlKey);
|
||||||
|
int fromTtl = (fromTtlI != null) ? fromTtlI[0] : 0;
|
||||||
|
Long fromWriteTime = (Long) fromValueMap.get(writeTimeKey);
|
||||||
|
|
||||||
|
if (toVal != null) {
|
||||||
|
if (fromVal != null) {
|
||||||
|
if (toVal == fromVal) {
|
||||||
|
// Case: object identity
|
||||||
|
// Goal: ensure write time and ttl are also in sync
|
||||||
|
if (fromWriteTime != null
|
||||||
|
&& fromWriteTime != 0L
|
||||||
|
&& (toWriteTime == null || fromWriteTime > toWriteTime)) {
|
||||||
|
((MapExportable) to).put(writeTimeKey, fromWriteTime);
|
||||||
|
}
|
||||||
|
if (fromTtl > 0 && fromTtl > toTtl) {
|
||||||
|
((MapExportable) to).put(ttlKey, fromTtl);
|
||||||
|
}
|
||||||
|
} else if (fromWriteTime != null && fromWriteTime != 0L) {
|
||||||
|
// Case: to exists and from exists
|
||||||
|
// Goal: copy over from -> to iff from.writeTime > to.writeTime
|
||||||
|
if (toWriteTime != null && toWriteTime != 0L) {
|
||||||
|
if (fromWriteTime > toWriteTime) {
|
||||||
|
((MapExportable) to).put(prop.getPropertyName(), fromVal);
|
||||||
|
((MapExportable) to).put(writeTimeKey, fromWriteTime);
|
||||||
|
if (fromTtl > 0) {
|
||||||
|
((MapExportable) to).put(ttlKey, fromTtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
((MapExportable) to).put(prop.getPropertyName(), fromVal);
|
||||||
|
((MapExportable) to).put(writeTimeKey, fromWriteTime);
|
||||||
|
if (fromTtl > 0) {
|
||||||
|
((MapExportable) to).put(ttlKey, fromTtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (toWriteTime == null || toWriteTime == 0L) {
|
||||||
|
// Caution, entering grey area...
|
||||||
|
if (!toVal.equals(fromVal)) {
|
||||||
|
// dangerous waters here, values diverge without information that enables resolution,
|
||||||
|
// policy (for now) is to move value from -> to anyway.
|
||||||
|
((MapExportable) to).put(prop.getPropertyName(), fromVal);
|
||||||
|
if (fromTtl > 0) {
|
||||||
|
((MapExportable) to).put(ttlKey, fromTtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Case: from exists, but to doesn't (it's null)
|
||||||
|
// Goal: copy over from -> to, include ttl and writeTime if present
|
||||||
|
if (fromVal != null) {
|
||||||
|
((MapExportable) to).put(prop.getPropertyName(), fromVal);
|
||||||
|
if (fromWriteTime != null && fromWriteTime != 0L) {
|
||||||
|
((MapExportable) to).put(writeTimeKey, fromWriteTime);
|
||||||
|
}
|
||||||
|
if (fromTtl > 0) {
|
||||||
|
((MapExportable) to).put(ttlKey, fromTtl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String schemaName(List<Facet> facets) {
|
||||||
|
return facets
|
||||||
|
.stream()
|
||||||
|
.filter(Facet::fixed)
|
||||||
|
.map(facet -> facet.value().toString())
|
||||||
|
.collect(Collectors.joining("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String writeTimeKey(HelenusProperty prop) {
|
||||||
|
return writeTimeKey(prop.getColumnName().toCql(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String ttlKey(HelenusProperty prop) {
|
||||||
|
return ttlKey(prop.getColumnName().toCql(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String writeTimeKey(String columnName) {
|
||||||
|
String key = "_" + columnName + "_writeTime";
|
||||||
|
return key.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String ttlKey(String columnName) {
|
||||||
|
String key = "_" + columnName + "_ttl";
|
||||||
|
return key.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
69
src/main/java/net/helenus/core/cache/Facet.java
vendored
Normal file
69
src/main/java/net/helenus/core/cache/Facet.java
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.cache;
|
||||||
|
|
||||||
|
/** An Entity is identifiable via one or more Facets */
|
||||||
|
public class Facet<T> {
|
||||||
|
private final String name;
|
||||||
|
private T value;
|
||||||
|
private boolean fixed = false;
|
||||||
|
private boolean alone = true;
|
||||||
|
private boolean combined = true;
|
||||||
|
|
||||||
|
public Facet(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Facet(String name, T value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Facet setFixed() {
|
||||||
|
fixed = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean fixed() {
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUniquelyIdentifyingWhenAlone(boolean alone) {
|
||||||
|
this.alone = alone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUniquelyIdentifyingWhenCombined(boolean combined) {
|
||||||
|
this.combined = combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean alone() {
|
||||||
|
return alone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean combined() {
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
}
|
463
src/main/java/net/helenus/core/cache/MapCache.java
vendored
Normal file
463
src/main/java/net/helenus/core/cache/MapCache.java
vendored
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
package net.helenus.core.cache;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import javax.cache.Cache;
|
||||||
|
import javax.cache.CacheManager;
|
||||||
|
import javax.cache.configuration.CacheEntryListenerConfiguration;
|
||||||
|
import javax.cache.configuration.Configuration;
|
||||||
|
import javax.cache.event.CacheEntryRemovedListener;
|
||||||
|
import javax.cache.integration.CacheLoader;
|
||||||
|
import javax.cache.integration.CompletionListener;
|
||||||
|
import javax.cache.processor.EntryProcessor;
|
||||||
|
import javax.cache.processor.EntryProcessorException;
|
||||||
|
import javax.cache.processor.EntryProcessorResult;
|
||||||
|
import javax.cache.processor.MutableEntry;
|
||||||
|
|
||||||
|
public class MapCache<K, V> implements Cache<K, V> {
|
||||||
|
private final CacheManager manager;
|
||||||
|
private final String name;
|
||||||
|
private Map<K, V> map = new ConcurrentHashMap<>();
|
||||||
|
private Set<CacheEntryRemovedListener<K, V>> cacheEntryRemovedListeners = new HashSet<>();
|
||||||
|
private CacheLoader<K, V> cacheLoader = null;
|
||||||
|
private boolean isReadThrough = false;
|
||||||
|
|
||||||
|
private static class MapConfiguration<K, V> implements Configuration<K, V> {
|
||||||
|
private static final long serialVersionUID = 6093947542772516209L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<K> getKeyType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<V> getValueType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStoreByValue() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapCache(
|
||||||
|
CacheManager manager, String name, CacheLoader<K, V> cacheLoader, boolean isReadThrough) {
|
||||||
|
this.manager = manager;
|
||||||
|
this.name = name;
|
||||||
|
this.cacheLoader = cacheLoader;
|
||||||
|
this.isReadThrough = isReadThrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public V get(K key) {
|
||||||
|
V value = null;
|
||||||
|
synchronized (map) {
|
||||||
|
value = map.get(key);
|
||||||
|
if (value == null && isReadThrough && cacheLoader != null) {
|
||||||
|
V loadedValue = cacheLoader.load(key);
|
||||||
|
if (loadedValue != null) {
|
||||||
|
map.put(key, loadedValue);
|
||||||
|
value = loadedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public Map<K, V> getAll(Set<? extends K> keys) {
|
||||||
|
Map<K, V> result = null;
|
||||||
|
synchronized (map) {
|
||||||
|
result = new HashMap<K, V>(keys.size());
|
||||||
|
Iterator<? extends K> it = keys.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
K key = it.next();
|
||||||
|
V value = map.get(key);
|
||||||
|
if (value != null) {
|
||||||
|
result.put(key, value);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keys.size() != 0 && isReadThrough && cacheLoader != null) {
|
||||||
|
Map<K, V> loadedValues = cacheLoader.loadAll(keys);
|
||||||
|
for (Map.Entry<K, V> entry : loadedValues.entrySet()) {
|
||||||
|
V v = entry.getValue();
|
||||||
|
if (v != null) {
|
||||||
|
K k = entry.getKey();
|
||||||
|
map.put(k, v);
|
||||||
|
result.put(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(K key) {
|
||||||
|
return map.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void loadAll(
|
||||||
|
Set<? extends K> keys, boolean replaceExistingValues, CompletionListener completionListener) {
|
||||||
|
if (cacheLoader != null) {
|
||||||
|
try {
|
||||||
|
synchronized (map) {
|
||||||
|
Map<K, V> loadedValues = cacheLoader.loadAll(keys);
|
||||||
|
for (Map.Entry<K, V> entry : loadedValues.entrySet()) {
|
||||||
|
V value = entry.getValue();
|
||||||
|
K key = entry.getKey();
|
||||||
|
if (value != null) {
|
||||||
|
boolean existsCurrently = map.containsKey(key);
|
||||||
|
if (!existsCurrently || replaceExistingValues) {
|
||||||
|
map.put(key, value);
|
||||||
|
keys.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (completionListener != null) {
|
||||||
|
completionListener.onException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (completionListener != null) {
|
||||||
|
if (keys.isEmpty()) {
|
||||||
|
completionListener.onCompletion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void put(K key, V value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public V getAndPut(K key, V value) {
|
||||||
|
V result = null;
|
||||||
|
synchronized (map) {
|
||||||
|
result = map.get(key);
|
||||||
|
if (result == null && isReadThrough && cacheLoader != null) {
|
||||||
|
V loadedValue = cacheLoader.load(key);
|
||||||
|
if (loadedValue != null) {
|
||||||
|
result = loadedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> map) {
|
||||||
|
synchronized (map) {
|
||||||
|
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||||
|
this.map.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean putIfAbsent(K key, V value) {
|
||||||
|
synchronized (map) {
|
||||||
|
if (!map.containsKey(key)) {
|
||||||
|
map.put(key, value);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean remove(K key) {
|
||||||
|
boolean removed = false;
|
||||||
|
synchronized (map) {
|
||||||
|
removed = map.remove(key) != null;
|
||||||
|
notifyRemovedListeners(key);
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean remove(K key, V oldValue) {
|
||||||
|
synchronized (map) {
|
||||||
|
V value = map.get(key);
|
||||||
|
if (value != null && oldValue.equals(value)) {
|
||||||
|
map.remove(key);
|
||||||
|
notifyRemovedListeners(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public V getAndRemove(K key) {
|
||||||
|
synchronized (map) {
|
||||||
|
V oldValue = null;
|
||||||
|
oldValue = map.get(key);
|
||||||
|
map.remove(key);
|
||||||
|
notifyRemovedListeners(key);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean replace(K key, V oldValue, V newValue) {
|
||||||
|
synchronized (map) {
|
||||||
|
V value = map.get(key);
|
||||||
|
if (value != null && oldValue.equals(value)) {
|
||||||
|
map.put(key, newValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean replace(K key, V value) {
|
||||||
|
synchronized (map) {
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
map.put(key, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public V getAndReplace(K key, V value) {
|
||||||
|
synchronized (map) {
|
||||||
|
V oldValue = map.get(key);
|
||||||
|
if (value != null && value.equals(oldValue)) {
|
||||||
|
map.put(key, value);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void removeAll(Set<? extends K> keys) {
|
||||||
|
synchronized (map) {
|
||||||
|
Iterator<? extends K> it = keys.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
K key = it.next();
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
map.remove(key);
|
||||||
|
} else {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyRemovedListeners(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void removeAll() {
|
||||||
|
synchronized (map) {
|
||||||
|
Set<K> keys = map.keySet();
|
||||||
|
map.clear();
|
||||||
|
notifyRemovedListeners(keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public <C extends Configuration<K, V>> C getConfiguration(Class<C> clazz) {
|
||||||
|
if (!MapConfiguration.class.isAssignableFrom(clazz)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public <T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)
|
||||||
|
throws EntryProcessorException {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public <T> Map<K, EntryProcessorResult<T>> invokeAll(
|
||||||
|
Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments) {
|
||||||
|
synchronized (map) {
|
||||||
|
for (K key : keys) {
|
||||||
|
V value = map.get(key);
|
||||||
|
if (value != null) {
|
||||||
|
entryProcessor.process(
|
||||||
|
new MutableEntry<K, V>() {
|
||||||
|
@Override
|
||||||
|
public boolean exists() {
|
||||||
|
return map.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
synchronized (map) {
|
||||||
|
V value = map.get(key);
|
||||||
|
if (value != null) {
|
||||||
|
map.remove(key);
|
||||||
|
notifyRemovedListeners(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return map.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(V value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public CacheManager getCacheManager() {
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void close() {}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T unwrap(Class<T> clazz) {
|
||||||
|
if (Map.class.isAssignableFrom(clazz)) {
|
||||||
|
return (T) map;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void registerCacheEntryListener(
|
||||||
|
CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
|
||||||
|
//cacheEntryRemovedListeners.add(cacheEntryListenerConfiguration.getCacheEntryListenerFactory().create());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void deregisterCacheEntryListener(
|
||||||
|
CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<K, V>> iterator() {
|
||||||
|
synchronized (map) {
|
||||||
|
return new Iterator<Entry<K, V>>() {
|
||||||
|
|
||||||
|
Iterator<Map.Entry<K, V>> entries = map.entrySet().iterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return entries.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> next() {
|
||||||
|
Map.Entry<K, V> entry = entries.next();
|
||||||
|
return new Entry<K, V>() {
|
||||||
|
K key = entry.getKey();
|
||||||
|
V value = entry.getValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyRemovedListeners(K key) {
|
||||||
|
// if (cacheEntryRemovedListeners != null) {
|
||||||
|
// cacheEntryRemovedListeners.forEach(listener -> listener.onRemoved())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyRemovedListeners(Set<? extends K> keys) {}
|
||||||
|
}
|
95
src/main/java/net/helenus/core/cache/UnboundFacet.java
vendored
Normal file
95
src/main/java/net/helenus/core/cache/UnboundFacet.java
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.cache;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.helenus.core.SchemaUtil;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
|
public class UnboundFacet extends Facet<String> {
|
||||||
|
|
||||||
|
private final List<HelenusProperty> properties;
|
||||||
|
private final boolean alone;
|
||||||
|
private final boolean combined;
|
||||||
|
|
||||||
|
public UnboundFacet(List<HelenusProperty> properties, boolean alone, boolean combined) {
|
||||||
|
super(SchemaUtil.createPrimaryKeyPhrase(properties));
|
||||||
|
this.properties = properties;
|
||||||
|
this.alone = alone;
|
||||||
|
this.combined = combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnboundFacet(List<HelenusProperty> properties) {
|
||||||
|
this(properties, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnboundFacet(HelenusProperty property, boolean alone, boolean combined) {
|
||||||
|
super(property.getPropertyName());
|
||||||
|
properties = new ArrayList<HelenusProperty>();
|
||||||
|
properties.add(property);
|
||||||
|
this.alone = alone;
|
||||||
|
this.combined = combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnboundFacet(HelenusProperty property) {
|
||||||
|
this(property, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HelenusProperty> getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binder binder() {
|
||||||
|
return new Binder(name(), properties, alone, combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Binder {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final boolean alone;
|
||||||
|
private final boolean combined;
|
||||||
|
private final List<HelenusProperty> properties = new ArrayList<HelenusProperty>();
|
||||||
|
private Map<HelenusProperty, Object> boundProperties = new HashMap<HelenusProperty, Object>();
|
||||||
|
|
||||||
|
Binder(String name, List<HelenusProperty> properties, boolean alone, boolean combined) {
|
||||||
|
this.name = name;
|
||||||
|
this.properties.addAll(properties);
|
||||||
|
this.alone = alone;
|
||||||
|
this.combined = combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binder setValueForProperty(HelenusProperty prop, Object value) {
|
||||||
|
properties.remove(prop);
|
||||||
|
boundProperties.put(prop, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBound() {
|
||||||
|
return properties.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundFacet bind() {
|
||||||
|
BoundFacet facet = new BoundFacet(name, boundProperties);
|
||||||
|
facet.setUniquelyIdentifyingWhenAlone(alone);
|
||||||
|
facet.setUniquelyIdentifyingWhenCombined(combined);
|
||||||
|
return facet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,97 +16,153 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.helenus.core.*;
|
import net.helenus.core.*;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.core.cache.UnboundFacet;
|
||||||
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperation<E, O>>
|
public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperation<E, O>>
|
||||||
extends
|
extends AbstractOperation<E, O> {
|
||||||
AbstractOperation<E, O> {
|
|
||||||
|
|
||||||
protected List<Filter<?>> filters = null;
|
protected List<Filter<?>> filters = null;
|
||||||
protected List<Filter<?>> ifFilters = null;
|
protected List<Filter<?>> ifFilters = null;
|
||||||
|
|
||||||
public AbstractFilterOperation(AbstractSessionOperations sessionOperations) {
|
public AbstractFilterOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O where(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, postulate));
|
addFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Getter<V> getter, Operator operator, V val) {
|
public <V> O where(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, operator, val));
|
addFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Filter<V> filter) {
|
public <V> O where(Filter<V> filter) {
|
||||||
|
|
||||||
addFilter(filter);
|
addFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O and(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, postulate));
|
addFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Getter<V> getter, Operator operator, V val) {
|
public <V> O and(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, operator, val));
|
addFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Filter<V> filter) {
|
public <V> O and(Filter<V> filter) {
|
||||||
|
|
||||||
addFilter(filter);
|
addFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addIfFilter(Filter.create(getter, postulate));
|
addIfFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
|
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addIfFilter(Filter.create(getter, operator, val));
|
addIfFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Filter<V> filter) {
|
public <V> O onlyIf(Filter<V> filter) {
|
||||||
|
|
||||||
addIfFilter(filter);
|
addIfFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFilter(Filter<?> filter) {
|
private void addFilter(Filter<?> filter) {
|
||||||
if (filters == null) {
|
if (filters == null) {
|
||||||
filters = new LinkedList<Filter<?>>();
|
filters = new LinkedList<Filter<?>>();
|
||||||
}
|
}
|
||||||
filters.add(filter);
|
filters.add(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addIfFilter(Filter<?> filter) {
|
private void addIfFilter(Filter<?> filter) {
|
||||||
if (ifFilters == null) {
|
if (ifFilters == null) {
|
||||||
ifFilters = new LinkedList<Filter<?>>();
|
ifFilters = new LinkedList<Filter<?>>();
|
||||||
}
|
}
|
||||||
ifFilters.add(filter);
|
ifFilters.add(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isIdempotentOperation() {
|
||||||
|
if (filters == null) {
|
||||||
|
return super.isIdempotentOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters
|
||||||
|
.stream()
|
||||||
|
.anyMatch(
|
||||||
|
filter -> {
|
||||||
|
HelenusPropertyNode node = filter.getNode();
|
||||||
|
if (node != null) {
|
||||||
|
HelenusProperty prop = node.getProperty();
|
||||||
|
if (prop != null) {
|
||||||
|
return prop.isIdempotent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
|| super.isIdempotentOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Facet> bindFacetValues(List<Facet> facets) {
|
||||||
|
if (facets == null) {
|
||||||
|
return new ArrayList<Facet>();
|
||||||
|
}
|
||||||
|
List<Facet> boundFacets = new ArrayList<>();
|
||||||
|
Map<HelenusProperty, Filter> filterMap = new HashMap<>(filters.size());
|
||||||
|
filters.forEach(f -> filterMap.put(f.getNode().getProperty(), f));
|
||||||
|
|
||||||
|
for (Facet facet : facets) {
|
||||||
|
if (facet instanceof UnboundFacet) {
|
||||||
|
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||||
|
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||||
|
if (filters != null) {
|
||||||
|
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||||
|
|
||||||
|
Filter filter = filterMap.get(prop);
|
||||||
|
if (filter != null) {
|
||||||
|
Object[] postulates = filter.postulateValues();
|
||||||
|
for (Object p : postulates) {
|
||||||
|
binder.setValueForProperty(prop, p.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (binder.isBound()) {
|
||||||
|
boundFacets.add(binder.bind());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boundFacets.add(facet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boundFacets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,97 +16,98 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import net.helenus.core.*;
|
import net.helenus.core.*;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
public abstract class AbstractFilterOptionalOperation<E, O extends AbstractFilterOptionalOperation<E, O>>
|
public abstract class AbstractFilterOptionalOperation<
|
||||||
extends
|
E, O extends AbstractFilterOptionalOperation<E, O>>
|
||||||
AbstractOptionalOperation<E, O> {
|
extends AbstractOptionalOperation<E, O> {
|
||||||
|
|
||||||
protected List<Filter<?>> filters = null;
|
protected Map<HelenusProperty, Filter<?>> filters = null;
|
||||||
protected List<Filter<?>> ifFilters = null;
|
protected List<Filter<?>> ifFilters = null;
|
||||||
|
|
||||||
public AbstractFilterOptionalOperation(AbstractSessionOperations sessionOperations) {
|
public AbstractFilterOptionalOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O where(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, postulate));
|
addFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Getter<V> getter, Operator operator, V val) {
|
public <V> O where(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, operator, val));
|
addFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Filter<V> filter) {
|
public <V> O where(Filter<V> filter) {
|
||||||
|
|
||||||
addFilter(filter);
|
addFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O and(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, postulate));
|
addFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Getter<V> getter, Operator operator, V val) {
|
public <V> O and(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, operator, val));
|
addFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Filter<V> filter) {
|
public <V> O and(Filter<V> filter) {
|
||||||
|
|
||||||
addFilter(filter);
|
addFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addIfFilter(Filter.create(getter, postulate));
|
addIfFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
|
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addIfFilter(Filter.create(getter, operator, val));
|
addIfFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Filter<V> filter) {
|
public <V> O onlyIf(Filter<V> filter) {
|
||||||
|
|
||||||
addIfFilter(filter);
|
addIfFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFilter(Filter<?> filter) {
|
private void addFilter(Filter<?> filter) {
|
||||||
if (filters == null) {
|
if (filters == null) {
|
||||||
filters = new LinkedList<Filter<?>>();
|
filters = new LinkedHashMap<HelenusProperty, Filter<?>>();
|
||||||
}
|
}
|
||||||
filters.add(filter);
|
filters.put(filter.getNode().getProperty(), filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addIfFilter(Filter<?> filter) {
|
|
||||||
if (ifFilters == null) {
|
|
||||||
ifFilters = new LinkedList<Filter<?>>();
|
|
||||||
}
|
|
||||||
ifFilters.add(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private void addIfFilter(Filter<?> filter) {
|
||||||
|
if (ifFilters == null) {
|
||||||
|
ifFilters = new LinkedList<Filter<?>>();
|
||||||
|
}
|
||||||
|
ifFilters.add(filter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,96 +16,98 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import net.helenus.core.*;
|
import net.helenus.core.*;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterStreamOperation<E, O>>
|
public abstract class AbstractFilterStreamOperation<
|
||||||
extends AbstractStreamOperation<E, O> {
|
E, O extends AbstractFilterStreamOperation<E, O>>
|
||||||
|
extends AbstractStreamOperation<E, O> {
|
||||||
|
|
||||||
protected List<Filter<?>> filters = null;
|
protected Map<HelenusProperty, Filter<?>> filters = null;
|
||||||
protected List<Filter<?>> ifFilters = null;
|
protected List<Filter<?>> ifFilters = null;
|
||||||
|
|
||||||
public AbstractFilterStreamOperation(AbstractSessionOperations sessionOperations) {
|
public AbstractFilterStreamOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O where(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, postulate));
|
addFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Getter<V> getter, Operator operator, V val) {
|
public <V> O where(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, operator, val));
|
if (val != null) addFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O where(Filter<V> filter) {
|
public <V> O where(Filter<V> filter) {
|
||||||
|
|
||||||
addFilter(filter);
|
addFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O and(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, postulate));
|
addFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Getter<V> getter, Operator operator, V val) {
|
public <V> O and(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addFilter(Filter.create(getter, operator, val));
|
if (val != null) addFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O and(Filter<V> filter) {
|
public <V> O and(Filter<V> filter) {
|
||||||
|
|
||||||
addFilter(filter);
|
addFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
|
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
|
||||||
|
|
||||||
addIfFilter(Filter.create(getter, postulate));
|
addIfFilter(Filter.create(getter, postulate));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
|
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
|
||||||
|
|
||||||
addIfFilter(Filter.create(getter, operator, val));
|
if (val != null) addIfFilter(Filter.create(getter, operator, val));
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> O onlyIf(Filter<V> filter) {
|
public <V> O onlyIf(Filter<V> filter) {
|
||||||
|
|
||||||
addIfFilter(filter);
|
addIfFilter(filter);
|
||||||
|
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFilter(Filter<?> filter) {
|
private void addFilter(Filter<?> filter) {
|
||||||
if (filters == null) {
|
if (filters == null) {
|
||||||
filters = new LinkedList<Filter<?>>();
|
filters = new LinkedHashMap<HelenusProperty, Filter<?>>();
|
||||||
}
|
}
|
||||||
filters.add(filter);
|
filters.put(filter.getNode().getProperty(), filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addIfFilter(Filter<?> filter) {
|
|
||||||
if (ifFilters == null) {
|
|
||||||
ifFilters = new LinkedList<Filter<?>>();
|
|
||||||
}
|
|
||||||
ifFilters.add(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private void addIfFilter(Filter<?> filter) {
|
||||||
|
if (ifFilters == null) {
|
||||||
|
ifFilters = new LinkedList<Filter<?>>();
|
||||||
|
}
|
||||||
|
ifFilters.add(filter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,106 +16,76 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import com.datastax.driver.core.PreparedStatement;
|
import com.codahale.metrics.Timer;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.ResultSetFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import com.google.common.base.Function;
|
import java.util.concurrent.CompletionException;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import java.util.concurrent.TimeoutException;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
import net.helenus.support.Fun;
|
import net.helenus.core.UnitOfWork;
|
||||||
import net.helenus.support.Scala;
|
|
||||||
import scala.concurrent.Future;
|
|
||||||
|
|
||||||
public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>> extends AbstractStatementOperation<E, O> {
|
public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>>
|
||||||
|
extends AbstractStatementOperation<E, O> {
|
||||||
|
|
||||||
public abstract E transform(ResultSet resultSet);
|
public AbstractOperation(AbstractSessionOperations sessionOperations) {
|
||||||
|
super(sessionOperations);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean cacheable() {
|
public abstract E transform(ResultSet resultSet);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCacheKey() {
|
public PreparedOperation<E> prepare() {
|
||||||
return "";
|
return new PreparedOperation<E>(prepareStatement(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractOperation(AbstractSessionOperations sessionOperations) {
|
public E sync() throws TimeoutException {
|
||||||
super(sessionOperations);
|
final Timer.Context context = requestLatency.time();
|
||||||
}
|
try {
|
||||||
|
ResultSet resultSet =
|
||||||
|
this.execute(
|
||||||
|
sessionOps, null, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
|
||||||
|
return transform(resultSet);
|
||||||
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public PreparedOperation<E> prepare() {
|
public E sync(UnitOfWork uow) throws TimeoutException {
|
||||||
return new PreparedOperation<E>(prepareStatement(), this);
|
if (uow == null) return sync();
|
||||||
}
|
|
||||||
|
|
||||||
public ListenableFuture<PreparedOperation<E>> prepareAsync() {
|
final Timer.Context context = requestLatency.time();
|
||||||
|
try {
|
||||||
|
ResultSet resultSet =
|
||||||
|
execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
|
||||||
|
E result = transform(resultSet);
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final O _this = (O) this;
|
public CompletableFuture<E> async() {
|
||||||
|
return CompletableFuture.<E>supplyAsync(
|
||||||
return Futures.transform(prepareStatementAsync(), new Function<PreparedStatement, PreparedOperation<E>>() {
|
() -> {
|
||||||
|
try {
|
||||||
@Override
|
return sync();
|
||||||
public PreparedOperation<E> apply(PreparedStatement preparedStatement) {
|
} catch (TimeoutException ex) {
|
||||||
return new PreparedOperation<E>(preparedStatement, _this);
|
throw new CompletionException(ex);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<PreparedOperation<E>> prepareFuture() {
|
|
||||||
return Scala.asFuture(prepareAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
public E sync() {
|
|
||||||
|
|
||||||
ResultSet resultSet = sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly();
|
|
||||||
E result = transform(resultSet);
|
|
||||||
if (cacheable()) {
|
|
||||||
sessionOps.cache(getCacheKey(), result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListenableFuture<E> async() {
|
|
||||||
|
|
||||||
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
|
|
||||||
|
|
||||||
ListenableFuture<E> future = Futures.transform(resultSetFuture, new Function<ResultSet, E>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public E apply(ResultSet resultSet) {
|
|
||||||
E result = transform(resultSet);
|
|
||||||
if (cacheable()) {
|
|
||||||
sessionOps.cache(getCacheKey(), result);
|
|
||||||
}
|
|
||||||
return transform(resultSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, sessionOps.getExecutor());
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<E> future() {
|
|
||||||
return Scala.asFuture(async());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A> Future<Fun.Tuple2<E, A>> future(A a) {
|
|
||||||
return Scala.asFuture(async(), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B> Future<Fun.Tuple3<E, A, B>> future(A a, B b) {
|
|
||||||
return Scala.asFuture(async(), a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B, C> Future<Fun.Tuple4<E, A, B, C>> future(A a, B b, C c) {
|
|
||||||
return Scala.asFuture(async(), a, b, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B, C, D> Future<Fun.Tuple5<E, A, B, C, D>> future(A a, B b, C c, D d) {
|
|
||||||
return Scala.asFuture(async(), a, b, c, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public CompletableFuture<E> async(UnitOfWork uow) {
|
||||||
|
if (uow == null) return async();
|
||||||
|
CompletableFuture<E> f =
|
||||||
|
CompletableFuture.<E>supplyAsync(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
return sync();
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
uow.addFuture(f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,118 +16,240 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.Optional;
|
import static net.helenus.core.HelenusSession.deleted;
|
||||||
|
|
||||||
|
import com.codahale.metrics.Timer;
|
||||||
import com.datastax.driver.core.PreparedStatement;
|
import com.datastax.driver.core.PreparedStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.ResultSetFuture;
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Stopwatch;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
|
import net.helenus.core.Helenus;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.core.cache.CacheUtil;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
import net.helenus.support.Fun;
|
import net.helenus.support.Fun;
|
||||||
import net.helenus.support.Scala;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
import scala.Option;
|
|
||||||
import scala.Some;
|
|
||||||
import scala.concurrent.Future;
|
|
||||||
|
|
||||||
public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>>
|
public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>>
|
||||||
extends
|
extends AbstractStatementOperation<E, O> {
|
||||||
AbstractStatementOperation<E, O> {
|
|
||||||
|
|
||||||
public AbstractOptionalOperation(AbstractSessionOperations sessionOperations) {
|
public AbstractOptionalOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Optional<E> transform(ResultSet resultSet);
|
public abstract Optional<E> transform(ResultSet resultSet);
|
||||||
|
|
||||||
public PreparedOptionalOperation<E> prepare() {
|
public PreparedOptionalOperation<E> prepare() {
|
||||||
return new PreparedOptionalOperation<E>(prepareStatement(), this);
|
return new PreparedOptionalOperation<E>(prepareStatement(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<PreparedOptionalOperation<E>> prepareAsync() {
|
public ListenableFuture<PreparedOptionalOperation<E>> prepareAsync() {
|
||||||
|
final O _this = (O) this;
|
||||||
|
return Futures.transform(
|
||||||
|
prepareStatementAsync(),
|
||||||
|
new Function<PreparedStatement, PreparedOptionalOperation<E>>() {
|
||||||
|
@Override
|
||||||
|
public PreparedOptionalOperation<E> apply(PreparedStatement preparedStatement) {
|
||||||
|
return new PreparedOptionalOperation<E>(preparedStatement, _this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final O _this = (O) this;
|
public Optional<E> sync() throws TimeoutException {
|
||||||
|
final Timer.Context context = requestLatency.time();
|
||||||
|
try {
|
||||||
|
Optional<E> result = Optional.empty();
|
||||||
|
E cacheResult = null;
|
||||||
|
boolean updateCache = isSessionCacheable() && !ignoreCache();
|
||||||
|
|
||||||
return Futures.transform(prepareStatementAsync(),
|
if (updateCache) {
|
||||||
new Function<PreparedStatement, PreparedOptionalOperation<E>>() {
|
List<Facet> facets = bindFacetValues();
|
||||||
|
if (facets != null && facets.size() > 0) {
|
||||||
|
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
cacheResult = (E) sessionOps.checkCache(tableName, facets);
|
||||||
|
if (cacheResult != null) {
|
||||||
|
result = Optional.of(cacheResult);
|
||||||
|
updateCache = false;
|
||||||
|
sessionCacheHits.mark();
|
||||||
|
cacheHits.mark();
|
||||||
|
} else {
|
||||||
|
sessionCacheMiss.mark();
|
||||||
|
cacheMiss.mark();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO(gburd): look in statement cache for results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
if (!result.isPresent()) {
|
||||||
public PreparedOptionalOperation<E> apply(PreparedStatement preparedStatement) {
|
// Formulate the query and execute it against the Cassandra cluster.
|
||||||
return new PreparedOptionalOperation<E>(preparedStatement, _this);
|
ResultSet resultSet =
|
||||||
}
|
this.execute(
|
||||||
|
sessionOps,
|
||||||
|
null,
|
||||||
|
queryExecutionTimeout,
|
||||||
|
queryTimeoutUnits,
|
||||||
|
showValues,
|
||||||
|
isSessionCacheable());
|
||||||
|
|
||||||
});
|
// Transform the query result set into the desired shape.
|
||||||
|
result = transform(resultSet);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
if (updateCache && result.isPresent()) {
|
||||||
|
E r = result.get();
|
||||||
|
Class<?> resultClass = r.getClass();
|
||||||
|
if (!(resultClass.getEnclosingClass() != null
|
||||||
|
&& resultClass.getEnclosingClass() == Fun.class)) {
|
||||||
|
List<Facet> facets = getFacets();
|
||||||
|
if (facets != null && facets.size() > 1) {
|
||||||
|
sessionOps.updateCache(r, facets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Future<PreparedOptionalOperation<E>> prepareFuture() {
|
public Optional<E> sync(UnitOfWork uow) throws TimeoutException {
|
||||||
return Scala.asFuture(prepareAsync());
|
if (uow == null) return sync();
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<E> sync() {
|
final Timer.Context context = requestLatency.time();
|
||||||
|
try {
|
||||||
|
|
||||||
ResultSet resultSet = sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly();
|
Optional<E> result = Optional.empty();
|
||||||
|
E cachedResult = null;
|
||||||
|
final boolean updateCache;
|
||||||
|
|
||||||
return transform(resultSet);
|
if (!ignoreCache()) {
|
||||||
}
|
Stopwatch timer = Stopwatch.createStarted();
|
||||||
|
try {
|
||||||
|
List<Facet> facets = bindFacetValues();
|
||||||
|
if (facets != null && facets.size() > 0) {
|
||||||
|
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
|
||||||
|
cachedResult = checkCache(uow, facets);
|
||||||
|
if (cachedResult != null) {
|
||||||
|
updateCache = false;
|
||||||
|
result = Optional.of(cachedResult);
|
||||||
|
uowCacheHits.mark();
|
||||||
|
cacheHits.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(1, 0);
|
||||||
|
} else {
|
||||||
|
uowCacheMiss.mark();
|
||||||
|
if (isSessionCacheable()) {
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
cachedResult = (E) sessionOps.checkCache(tableName, facets);
|
||||||
|
if (cachedResult != null) {
|
||||||
|
Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
|
||||||
|
if (Helenus.entity(iface).isDraftable()) {
|
||||||
|
result = Optional.of(cachedResult);
|
||||||
|
} else {
|
||||||
|
result =
|
||||||
|
Optional.of(
|
||||||
|
(E)
|
||||||
|
SerializationUtils.<Serializable>clone(
|
||||||
|
(Serializable) cachedResult));
|
||||||
|
}
|
||||||
|
updateCache = false;
|
||||||
|
sessionCacheHits.mark();
|
||||||
|
cacheHits.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(1, 0);
|
||||||
|
} else {
|
||||||
|
updateCache = true;
|
||||||
|
sessionCacheMiss.mark();
|
||||||
|
cacheMiss.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(-1, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateCache = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO(gburd): look in statement cache for results
|
||||||
|
updateCache = false; //true;
|
||||||
|
cacheMiss.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(-1, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateCache = false;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
timer.stop();
|
||||||
|
uow.addCacheLookupTime(timer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateCache = false;
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<Optional<E>> async() {
|
// Check to see if we fetched the object from the cache
|
||||||
|
if (result.isPresent()) {
|
||||||
|
// If we fetched the `deleted` object then the result is null (really
|
||||||
|
// Optional.empty()).
|
||||||
|
if (result.get() == deleted) {
|
||||||
|
result = Optional.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
|
// Formulate the query and execute it against the Cassandra cluster.
|
||||||
|
ResultSet resultSet =
|
||||||
|
execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
|
||||||
|
|
||||||
ListenableFuture<Optional<E>> future = Futures.transform(resultSetFuture,
|
// Transform the query result set into the desired shape.
|
||||||
new Function<ResultSet, Optional<E>>() {
|
result = transform(resultSet);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// If we have a result, it wasn't from the UOW cache, and we're caching things
|
||||||
public Optional<E> apply(ResultSet resultSet) {
|
// then we need to put this result into the cache for future requests to find.
|
||||||
return transform(resultSet);
|
if (updateCache && result.isPresent()) {
|
||||||
}
|
E r = result.get();
|
||||||
|
if (!(r instanceof Fun) && r != deleted) {
|
||||||
|
cacheUpdate(uow, r, getFacets());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}, sessionOps.getExecutor());
|
return result;
|
||||||
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return future;
|
public CompletableFuture<Optional<E>> async() {
|
||||||
}
|
return CompletableFuture.<Optional<E>>supplyAsync(
|
||||||
|
() -> {
|
||||||
public ListenableFuture<Option<E>> asyncForScala() {
|
try {
|
||||||
|
return sync();
|
||||||
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
|
} catch (TimeoutException ex) {
|
||||||
|
throw new CompletionException(ex);
|
||||||
ListenableFuture<Option<E>> future = Futures.transform(resultSetFuture, new Function<ResultSet, Option<E>>() {
|
}
|
||||||
|
});
|
||||||
@Override
|
}
|
||||||
public Option<E> apply(ResultSet resultSet) {
|
|
||||||
Optional<E> optional = transform(resultSet);
|
|
||||||
if (optional.isPresent()) {
|
|
||||||
return new Some<E>(optional.get());
|
|
||||||
} else {
|
|
||||||
return Option.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, sessionOps.getExecutor());
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
public Future<Option<E>> future() {
|
|
||||||
return Scala.asFuture(asyncForScala());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A> Future<Fun.Tuple2<Option<E>, A>> future(A a) {
|
|
||||||
return Scala.asFuture(asyncForScala(), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B> Future<Fun.Tuple3<Option<E>, A, B>> future(A a, B b) {
|
|
||||||
return Scala.asFuture(asyncForScala(), a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B, C> Future<Fun.Tuple4<Option<E>, A, B, C>> future(A a, B b, C c) {
|
|
||||||
return Scala.asFuture(asyncForScala(), a, b, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B, C, D> Future<Fun.Tuple5<Option<E>, A, B, C, D>> future(A a, B b, C c, D d) {
|
|
||||||
return Scala.asFuture(asyncForScala(), a, b, c, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public CompletableFuture<Optional<E>> async(UnitOfWork uow) {
|
||||||
|
if (uow == null) return async();
|
||||||
|
CompletableFuture<Optional<E>> f =
|
||||||
|
CompletableFuture.<Optional<E>>supplyAsync(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
return sync();
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
uow.addFuture(f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,9 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.ConsistencyLevel;
|
import com.datastax.driver.core.ConsistencyLevel;
|
||||||
import com.datastax.driver.core.PreparedStatement;
|
import com.datastax.driver.core.PreparedStatement;
|
||||||
import com.datastax.driver.core.RegularStatement;
|
import com.datastax.driver.core.RegularStatement;
|
||||||
|
@ -28,211 +26,331 @@ import com.datastax.driver.core.policies.FallthroughRetryPolicy;
|
||||||
import com.datastax.driver.core.policies.RetryPolicy;
|
import com.datastax.driver.core.policies.RetryPolicy;
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.core.cache.UnboundFacet;
|
||||||
|
import net.helenus.core.reflect.MapExportable;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
import net.helenus.mapping.value.BeanColumnValueProvider;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
import net.helenus.support.Scala;
|
|
||||||
import scala.concurrent.Future;
|
|
||||||
|
|
||||||
public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>> {
|
public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>>
|
||||||
|
extends Operation<E> {
|
||||||
|
private boolean ignoreCache = false;
|
||||||
|
private ConsistencyLevel consistencyLevel;
|
||||||
|
private ConsistencyLevel serialConsistencyLevel;
|
||||||
|
private RetryPolicy retryPolicy;
|
||||||
|
private boolean enableTracing = false;
|
||||||
|
private long[] defaultTimestamp = null;
|
||||||
|
private int[] fetchSize = null;
|
||||||
|
protected boolean idempotent = false;
|
||||||
|
|
||||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
public AbstractStatementOperation(AbstractSessionOperations sessionOperations) {
|
||||||
|
super(sessionOperations);
|
||||||
|
this.consistencyLevel = sessionOperations.getDefaultConsistencyLevel();
|
||||||
|
this.idempotent = sessionOperations.getDefaultQueryIdempotency();
|
||||||
|
}
|
||||||
|
|
||||||
protected final AbstractSessionOperations sessionOps;
|
public abstract Statement buildStatement(boolean cached);
|
||||||
|
|
||||||
public abstract Statement buildStatement();
|
public O uncached(boolean enabled) {
|
||||||
|
ignoreCache = !enabled;
|
||||||
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean showValues = true;
|
public O uncached() {
|
||||||
private ConsistencyLevel consistencyLevel;
|
ignoreCache = true;
|
||||||
private ConsistencyLevel serialConsistencyLevel;
|
return (O) this;
|
||||||
private RetryPolicy retryPolicy;
|
}
|
||||||
private boolean enableTracing = false;
|
|
||||||
private long[] defaultTimestamp = null;
|
|
||||||
private int[] fetchSize = null;
|
|
||||||
|
|
||||||
public AbstractStatementOperation(AbstractSessionOperations sessionOperations) {
|
public O showValues(boolean enabled) {
|
||||||
this.sessionOps = sessionOperations;
|
this.showValues = enabled;
|
||||||
}
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
public O showValues(boolean enabled) {
|
public O defaultTimestamp(long timestamp) {
|
||||||
this.showValues = enabled;
|
this.defaultTimestamp = new long[1];
|
||||||
return (O) this;
|
this.defaultTimestamp[0] = timestamp;
|
||||||
}
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
public O defaultTimestamp(long timestamp) {
|
public O retryPolicy(RetryPolicy retryPolicy) {
|
||||||
this.defaultTimestamp = new long[1];
|
this.retryPolicy = retryPolicy;
|
||||||
this.defaultTimestamp[0] = timestamp;
|
return (O) this;
|
||||||
return (O) this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public O retryPolicy(RetryPolicy retryPolicy) {
|
public O defaultRetryPolicy() {
|
||||||
this.retryPolicy = retryPolicy;
|
this.retryPolicy = DefaultRetryPolicy.INSTANCE;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O defaultRetryPolicy() {
|
public O idempotent() {
|
||||||
this.retryPolicy = DefaultRetryPolicy.INSTANCE;
|
this.idempotent = true;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O downgradingConsistencyRetryPolicy() {
|
public O isIdempotent(boolean idempotent) {
|
||||||
this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
|
this.idempotent = idempotent;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O fallthroughRetryPolicy() {
|
public O downgradingConsistencyRetryPolicy() {
|
||||||
this.retryPolicy = FallthroughRetryPolicy.INSTANCE;
|
this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O consistency(ConsistencyLevel level) {
|
public O fallthroughRetryPolicy() {
|
||||||
this.consistencyLevel = level;
|
this.retryPolicy = FallthroughRetryPolicy.INSTANCE;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O consistencyAny() {
|
public O consistency(ConsistencyLevel level) {
|
||||||
this.consistencyLevel = ConsistencyLevel.ANY;
|
this.consistencyLevel = level;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O consistencyOne() {
|
public O consistencyAny() {
|
||||||
this.consistencyLevel = ConsistencyLevel.ONE;
|
this.consistencyLevel = ConsistencyLevel.ANY;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O consistencyQuorum() {
|
public O consistencyOne() {
|
||||||
this.consistencyLevel = ConsistencyLevel.QUORUM;
|
this.consistencyLevel = ConsistencyLevel.ONE;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O consistencyAll() {
|
public O consistencyQuorum() {
|
||||||
this.consistencyLevel = ConsistencyLevel.ALL;
|
this.consistencyLevel = ConsistencyLevel.QUORUM;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O serialConsistency(ConsistencyLevel level) {
|
public O consistencyAll() {
|
||||||
this.serialConsistencyLevel = level;
|
this.consistencyLevel = ConsistencyLevel.ALL;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O serialConsistencyAny() {
|
public O consistencyLocalOne() {
|
||||||
this.serialConsistencyLevel = ConsistencyLevel.ANY;
|
this.consistencyLevel = ConsistencyLevel.LOCAL_ONE;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O serialConsistencyOne() {
|
public O consistencyLocalQuorum() {
|
||||||
this.serialConsistencyLevel = ConsistencyLevel.ONE;
|
this.consistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O serialConsistencyQuorum() {
|
public O consistencyEachQuorum() {
|
||||||
this.serialConsistencyLevel = ConsistencyLevel.QUORUM;
|
this.consistencyLevel = ConsistencyLevel.EACH_QUORUM;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O serialConsistencyAll() {
|
public O serialConsistency(ConsistencyLevel level) {
|
||||||
this.serialConsistencyLevel = ConsistencyLevel.ALL;
|
this.serialConsistencyLevel = level;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O disableTracing() {
|
public O serialConsistencyAny() {
|
||||||
this.enableTracing = false;
|
this.serialConsistencyLevel = ConsistencyLevel.ANY;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O enableTracing() {
|
public O serialConsistencyOne() {
|
||||||
this.enableTracing = true;
|
this.serialConsistencyLevel = ConsistencyLevel.ONE;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O tracing(boolean enable) {
|
public O serialConsistencyQuorum() {
|
||||||
this.enableTracing = enable;
|
this.serialConsistencyLevel = ConsistencyLevel.QUORUM;
|
||||||
return (O) this;
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public O fetchSize(int fetchSize) {
|
public O serialConsistencyAll() {
|
||||||
this.fetchSize = new int[1];
|
this.serialConsistencyLevel = ConsistencyLevel.ALL;
|
||||||
this.fetchSize[0] = fetchSize;
|
return (O) this;
|
||||||
return (O) this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected Statement options(Statement statement) {
|
public O serialConsistencyLocal() {
|
||||||
|
this.serialConsistencyLevel = ConsistencyLevel.LOCAL_SERIAL;
|
||||||
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
if (defaultTimestamp != null) {
|
public O serialConsistencyLocalQuorum() {
|
||||||
statement.setDefaultTimestamp(defaultTimestamp[0]);
|
this.serialConsistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
|
||||||
}
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
if (consistencyLevel != null) {
|
public O disableTracing() {
|
||||||
statement.setConsistencyLevel(consistencyLevel);
|
this.enableTracing = false;
|
||||||
}
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
if (serialConsistencyLevel != null) {
|
public O enableTracing() {
|
||||||
statement.setSerialConsistencyLevel(serialConsistencyLevel);
|
this.enableTracing = true;
|
||||||
}
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
if (retryPolicy != null) {
|
public O tracing(boolean enable) {
|
||||||
statement.setRetryPolicy(retryPolicy);
|
this.enableTracing = enable;
|
||||||
}
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
if (enableTracing) {
|
public O fetchSize(int fetchSize) {
|
||||||
statement.enableTracing();
|
this.fetchSize = new int[1];
|
||||||
} else {
|
this.fetchSize[0] = fetchSize;
|
||||||
statement.disableTracing();
|
return (O) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fetchSize != null) {
|
public O queryTimeoutMs(long ms) {
|
||||||
statement.setFetchSize(fetchSize[0]);
|
this.queryExecutionTimeout = ms;
|
||||||
}
|
this.queryTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||||
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
return statement;
|
public O queryTimeout(long timeout, TimeUnit units) {
|
||||||
}
|
this.queryExecutionTimeout = timeout;
|
||||||
|
this.queryTimeoutUnits = units;
|
||||||
|
return (O) this;
|
||||||
|
}
|
||||||
|
|
||||||
public Statement statement() {
|
public Statement options(Statement statement) {
|
||||||
return buildStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String cql() {
|
if (defaultTimestamp != null) {
|
||||||
Statement statement = buildStatement();
|
statement.setDefaultTimestamp(defaultTimestamp[0]);
|
||||||
if (statement == null) return "";
|
}
|
||||||
if (statement instanceof BuiltStatement) {
|
|
||||||
BuiltStatement buildStatement = (BuiltStatement) statement;
|
|
||||||
return buildStatement.setForceNoValues(true).getQueryString();
|
|
||||||
} else {
|
|
||||||
return statement.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PreparedStatement prepareStatement() {
|
if (consistencyLevel != null) {
|
||||||
|
statement.setConsistencyLevel(consistencyLevel);
|
||||||
|
}
|
||||||
|
|
||||||
Statement statement = buildStatement();
|
if (serialConsistencyLevel != null) {
|
||||||
|
statement.setSerialConsistencyLevel(serialConsistencyLevel);
|
||||||
|
}
|
||||||
|
|
||||||
if (statement instanceof RegularStatement) {
|
if (retryPolicy != null) {
|
||||||
|
statement.setRetryPolicy(retryPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
RegularStatement regularStatement = (RegularStatement) statement;
|
if (enableTracing) {
|
||||||
|
statement.enableTracing();
|
||||||
|
} else {
|
||||||
|
statement.disableTracing();
|
||||||
|
}
|
||||||
|
|
||||||
return sessionOps.prepare(regularStatement);
|
if (fetchSize != null) {
|
||||||
}
|
statement.setFetchSize(fetchSize[0]);
|
||||||
|
}
|
||||||
|
|
||||||
throw new HelenusException("only RegularStatements can be prepared");
|
if (isIdempotentOperation()) {
|
||||||
}
|
statement.setIdempotent(true);
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<PreparedStatement> prepareStatementAsync() {
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
Statement statement = buildStatement();
|
@Override
|
||||||
|
protected boolean isIdempotentOperation() {
|
||||||
|
return idempotent;
|
||||||
|
}
|
||||||
|
|
||||||
if (statement instanceof RegularStatement) {
|
public Statement statement() {
|
||||||
|
return buildStatement(false);
|
||||||
|
}
|
||||||
|
|
||||||
RegularStatement regularStatement = (RegularStatement) statement;
|
public String cql() {
|
||||||
|
Statement statement = buildStatement(false);
|
||||||
|
if (statement == null) return "";
|
||||||
|
if (statement instanceof BuiltStatement) {
|
||||||
|
BuiltStatement buildStatement = (BuiltStatement) statement;
|
||||||
|
return buildStatement.setForceNoValues(true).getQueryString();
|
||||||
|
} else {
|
||||||
|
return statement.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sessionOps.prepareAsync(regularStatement);
|
public PreparedStatement prepareStatement() {
|
||||||
|
|
||||||
}
|
Statement statement = buildStatement(true);
|
||||||
|
|
||||||
throw new HelenusException("only RegularStatements can be prepared");
|
if (statement instanceof RegularStatement) {
|
||||||
}
|
|
||||||
|
|
||||||
public Future<PreparedStatement> prepareStatementFuture() {
|
RegularStatement regularStatement = (RegularStatement) statement;
|
||||||
return Scala.asFuture(prepareStatementAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return sessionOps.prepare(regularStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new HelenusException("only RegularStatements can be prepared");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<PreparedStatement> prepareStatementAsync() {
|
||||||
|
|
||||||
|
Statement statement = buildStatement(true);
|
||||||
|
|
||||||
|
if (statement instanceof RegularStatement) {
|
||||||
|
|
||||||
|
RegularStatement regularStatement = (RegularStatement) statement;
|
||||||
|
|
||||||
|
return sessionOps.prepareAsync(regularStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new HelenusException("only RegularStatements can be prepared");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean ignoreCache() {
|
||||||
|
return ignoreCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected E checkCache(UnitOfWork uow, List<Facet> facets) {
|
||||||
|
E result = null;
|
||||||
|
Optional<Object> optionalCachedResult = Optional.empty();
|
||||||
|
|
||||||
|
if (!facets.isEmpty()) {
|
||||||
|
optionalCachedResult = uow.cacheLookup(facets);
|
||||||
|
if (optionalCachedResult.isPresent()) {
|
||||||
|
result = (E) optionalCachedResult.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object cacheUpdate(UnitOfWork uow, E pojo, List<Facet> identifyingFacets) {
|
||||||
|
List<Facet> facets = new ArrayList<>();
|
||||||
|
Map<String, Object> valueMap =
|
||||||
|
pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
|
||||||
|
|
||||||
|
for (Facet facet : identifyingFacets) {
|
||||||
|
if (facet instanceof UnboundFacet) {
|
||||||
|
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||||
|
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||||
|
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||||
|
Object value;
|
||||||
|
if (valueMap == null) {
|
||||||
|
value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
|
||||||
|
if (value != null) {
|
||||||
|
binder.setValueForProperty(prop, value.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = valueMap.get(prop.getPropertyName());
|
||||||
|
if (value != null) {
|
||||||
|
binder.setValueForProperty(prop, value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (binder.isBound()) {
|
||||||
|
facets.add(binder.bind());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
facets.add(facet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the value (pojo), the statement key, and the fully bound facets.
|
||||||
|
return uow.cacheUpdate(pojo, facets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,123 +16,246 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import static net.helenus.core.HelenusSession.deleted;
|
||||||
|
|
||||||
|
import com.codahale.metrics.Timer;
|
||||||
import com.datastax.driver.core.PreparedStatement;
|
import com.datastax.driver.core.PreparedStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.ResultSetFuture;
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.base.Stopwatch;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
|
import net.helenus.core.Helenus;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.core.cache.CacheUtil;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
import net.helenus.support.Fun;
|
import net.helenus.support.Fun;
|
||||||
import net.helenus.support.Scala;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
import scala.concurrent.Future;
|
|
||||||
|
|
||||||
public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>>
|
public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>>
|
||||||
extends
|
extends AbstractStatementOperation<E, O> {
|
||||||
AbstractStatementOperation<E, O> {
|
|
||||||
|
|
||||||
public AbstractStreamOperation(AbstractSessionOperations sessionOperations) {
|
public AbstractStreamOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Stream<E> transform(ResultSet resultSet);
|
public abstract Stream<E> transform(ResultSet resultSet);
|
||||||
|
|
||||||
public PreparedStreamOperation<E> prepare() {
|
public PreparedStreamOperation<E> prepare() {
|
||||||
return new PreparedStreamOperation<E>(prepareStatement(), this);
|
return new PreparedStreamOperation<E>(prepareStatement(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<PreparedStreamOperation<E>> prepareAsync() {
|
public ListenableFuture<PreparedStreamOperation<E>> prepareAsync() {
|
||||||
|
final O _this = (O) this;
|
||||||
final O _this = (O) this;
|
return Futures.transform(
|
||||||
|
prepareStatementAsync(),
|
||||||
return Futures.transform(prepareStatementAsync(),
|
new Function<PreparedStatement, PreparedStreamOperation<E>>() {
|
||||||
new Function<PreparedStatement, PreparedStreamOperation<E>>() {
|
@Override
|
||||||
|
public PreparedStreamOperation<E> apply(PreparedStatement preparedStatement) {
|
||||||
@Override
|
return new PreparedStreamOperation<E>(preparedStatement, _this);
|
||||||
public PreparedStreamOperation<E> apply(PreparedStatement preparedStatement) {
|
}
|
||||||
return new PreparedStreamOperation<E>(preparedStatement, _this);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<PreparedStreamOperation<E>> prepareFuture() {
|
|
||||||
return Scala.asFuture(prepareAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream<E> sync() {
|
|
||||||
ListenableFuture<Stream<E>> future = async();
|
|
||||||
Futures.addCallback(future, new FutureCallback<String>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(String contents) {
|
|
||||||
//...process web site contents
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Throwable throwable) {
|
|
||||||
log.error("Exception in task", throwable);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Stream<E>> async() {
|
public Stream<E> sync() throws TimeoutException {
|
||||||
|
final Timer.Context context = requestLatency.time();
|
||||||
|
try {
|
||||||
|
Stream<E> resultStream = null;
|
||||||
|
E cacheResult = null;
|
||||||
|
boolean updateCache = isSessionCacheable();
|
||||||
|
|
||||||
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
|
if (!ignoreCache() && isSessionCacheable()) {
|
||||||
|
List<Facet> facets = bindFacetValues();
|
||||||
|
if (facets != null && facets.size() > 0) {
|
||||||
|
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
cacheResult = (E) sessionOps.checkCache(tableName, facets);
|
||||||
|
if (cacheResult != null) {
|
||||||
|
resultStream = Stream.of(cacheResult);
|
||||||
|
updateCache = false;
|
||||||
|
sessionCacheHits.mark();
|
||||||
|
cacheHits.mark();
|
||||||
|
} else {
|
||||||
|
sessionCacheMiss.mark();
|
||||||
|
cacheMiss.mark();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO(gburd): look in statement cache for results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListenableFuture<Stream<E>> future = Futures.transform(resultSetFuture, new Function<ResultSet, Stream<E>>() {
|
if (resultStream == null) {
|
||||||
|
// Formulate the query and execute it against the Cassandra cluster.
|
||||||
|
ResultSet resultSet =
|
||||||
|
this.execute(
|
||||||
|
sessionOps,
|
||||||
|
null,
|
||||||
|
queryExecutionTimeout,
|
||||||
|
queryTimeoutUnits,
|
||||||
|
showValues,
|
||||||
|
isSessionCacheable());
|
||||||
|
|
||||||
@Override
|
// Transform the query result set into the desired shape.
|
||||||
public Stream<E> apply(ResultSet resultSet) {
|
resultStream = transform(resultSet);
|
||||||
return transform(resultSet);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}, sessionOps.getExecutor());
|
if (updateCache && resultStream != null) {
|
||||||
|
List<Facet> facets = getFacets();
|
||||||
|
if (facets != null && facets.size() > 1) {
|
||||||
|
List<E> again = new ArrayList<>();
|
||||||
|
resultStream.forEach(
|
||||||
|
result -> {
|
||||||
|
Class<?> resultClass = result.getClass();
|
||||||
|
if (!(resultClass.getEnclosingClass() != null
|
||||||
|
&& resultClass.getEnclosingClass() == Fun.class)) {
|
||||||
|
sessionOps.updateCache(result, facets);
|
||||||
|
}
|
||||||
|
again.add(result);
|
||||||
|
});
|
||||||
|
resultStream = again.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultStream;
|
||||||
|
|
||||||
return future;
|
} finally {
|
||||||
}
|
context.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<scala.collection.immutable.Stream<E>> asyncForScala() {
|
public Stream<E> sync(UnitOfWork uow) throws TimeoutException {
|
||||||
|
if (uow == null) return sync();
|
||||||
|
|
||||||
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
|
final Timer.Context context = requestLatency.time();
|
||||||
|
try {
|
||||||
|
Stream<E> resultStream = null;
|
||||||
|
E cachedResult = null;
|
||||||
|
final boolean updateCache;
|
||||||
|
|
||||||
ListenableFuture<scala.collection.immutable.Stream<E>> future = Futures.transform(resultSetFuture,
|
if (!ignoreCache()) {
|
||||||
new Function<ResultSet, scala.collection.immutable.Stream<E>>() {
|
Stopwatch timer = Stopwatch.createStarted();
|
||||||
|
try {
|
||||||
|
List<Facet> facets = bindFacetValues();
|
||||||
|
if (facets != null && facets.size() > 0) {
|
||||||
|
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
|
||||||
|
cachedResult = checkCache(uow, facets);
|
||||||
|
if (cachedResult != null) {
|
||||||
|
updateCache = false;
|
||||||
|
resultStream = Stream.of(cachedResult);
|
||||||
|
uowCacheHits.mark();
|
||||||
|
cacheHits.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(1, 0);
|
||||||
|
} else {
|
||||||
|
uowCacheMiss.mark();
|
||||||
|
if (isSessionCacheable()) {
|
||||||
|
String tableName = CacheUtil.schemaName(facets);
|
||||||
|
cachedResult = (E) sessionOps.checkCache(tableName, facets);
|
||||||
|
if (cachedResult != null) {
|
||||||
|
Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
|
||||||
|
E result = null;
|
||||||
|
if (Helenus.entity(iface).isDraftable()) {
|
||||||
|
result = cachedResult;
|
||||||
|
} else {
|
||||||
|
result =
|
||||||
|
(E) SerializationUtils.<Serializable>clone((Serializable) cachedResult);
|
||||||
|
}
|
||||||
|
updateCache = false;
|
||||||
|
resultStream = Stream.of(result);
|
||||||
|
sessionCacheHits.mark();
|
||||||
|
cacheHits.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(1, 0);
|
||||||
|
} else {
|
||||||
|
updateCache = true;
|
||||||
|
sessionCacheMiss.mark();
|
||||||
|
cacheMiss.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(-1, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateCache = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO(gburd): look in statement cache for results
|
||||||
|
updateCache = false; //true;
|
||||||
|
cacheMiss.mark();
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(-1, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateCache = false;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
timer.stop();
|
||||||
|
uow.addCacheLookupTime(timer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateCache = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// Check to see if we fetched the object from the cache
|
||||||
public scala.collection.immutable.Stream<E> apply(ResultSet resultSet) {
|
if (resultStream == null) {
|
||||||
Stream<E> stream = transform(resultSet);
|
ResultSet resultSet =
|
||||||
return scala.collection.JavaConversions.asScalaIterator(stream.iterator()).toStream();
|
execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
|
||||||
}
|
resultStream = transform(resultSet);
|
||||||
|
}
|
||||||
|
|
||||||
}, sessionOps.getExecutor());
|
// If we have a result and we're caching then we need to put it into the cache
|
||||||
|
// for future requests to find.
|
||||||
|
if (resultStream != null) {
|
||||||
|
if (updateCache) {
|
||||||
|
List<E> again = new ArrayList<>();
|
||||||
|
List<Facet> facets = getFacets();
|
||||||
|
resultStream.forEach(
|
||||||
|
result -> {
|
||||||
|
Class<?> resultClass = result.getClass();
|
||||||
|
if (result != deleted
|
||||||
|
&& !(resultClass.getEnclosingClass() != null
|
||||||
|
&& resultClass.getEnclosingClass() == Fun.class)) {
|
||||||
|
result = (E) cacheUpdate(uow, result, facets);
|
||||||
|
}
|
||||||
|
again.add(result);
|
||||||
|
});
|
||||||
|
resultStream = again.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return future;
|
return resultStream;
|
||||||
}
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Future<scala.collection.immutable.Stream<E>> future() {
|
public CompletableFuture<Stream<E>> async() {
|
||||||
return Scala.asFuture(asyncForScala());
|
return CompletableFuture.<Stream<E>>supplyAsync(
|
||||||
}
|
() -> {
|
||||||
|
try {
|
||||||
public <A> Future<Fun.Tuple2<scala.collection.immutable.Stream<E>, A>> future(A a) {
|
return sync();
|
||||||
return Scala.asFuture(asyncForScala(), a);
|
} catch (TimeoutException ex) {
|
||||||
}
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
public <A, B> Future<Fun.Tuple3<scala.collection.immutable.Stream<E>, A, B>> future(A a, B b) {
|
});
|
||||||
return Scala.asFuture(asyncForScala(), a, b);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B, C> Future<Fun.Tuple4<scala.collection.immutable.Stream<E>, A, B, C>> future(A a, B b, C c) {
|
|
||||||
return Scala.asFuture(asyncForScala(), a, b, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A, B, C, D> Future<Fun.Tuple5<scala.collection.immutable.Stream<E>, A, B, C, D>> future(A a, B b, C c,
|
|
||||||
D d) {
|
|
||||||
return Scala.asFuture(asyncForScala(), a, b, c, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public CompletableFuture<Stream<E>> async(UnitOfWork uow) {
|
||||||
|
if (uow == null) return async();
|
||||||
|
CompletableFuture<Stream<E>> f =
|
||||||
|
CompletableFuture.<Stream<E>>supplyAsync(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
return sync();
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
throw new CompletionException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
uow.addFuture(f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
140
src/main/java/net/helenus/core/operation/BatchOperation.java
Normal file
140
src/main/java/net/helenus/core/operation/BatchOperation.java
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.operation;
|
||||||
|
|
||||||
|
import com.codahale.metrics.Timer;
|
||||||
|
import com.datastax.driver.core.AtomicMonotonicTimestampGenerator;
|
||||||
|
import com.datastax.driver.core.BatchStatement;
|
||||||
|
import com.datastax.driver.core.ResultSet;
|
||||||
|
import com.datastax.driver.core.TimestampGenerator;
|
||||||
|
import com.google.common.base.Stopwatch;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
|
public class BatchOperation extends Operation<Long> {
|
||||||
|
//TODO(gburd): find the way to get the driver's timestamp generator
|
||||||
|
private static final TimestampGenerator timestampGenerator =
|
||||||
|
new AtomicMonotonicTimestampGenerator();
|
||||||
|
|
||||||
|
private final BatchStatement batch;
|
||||||
|
private List<AbstractOperation<?, ?>> operations = new ArrayList<AbstractOperation<?, ?>>();
|
||||||
|
private boolean logged = true;
|
||||||
|
|
||||||
|
public BatchOperation(AbstractSessionOperations sessionOperations) {
|
||||||
|
super(sessionOperations);
|
||||||
|
batch = new BatchStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(AbstractOperation<?, ?> operation) {
|
||||||
|
operations.add(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchStatement buildStatement(boolean cached) {
|
||||||
|
batch.addAll(
|
||||||
|
operations.stream().map(o -> o.buildStatement(cached)).collect(Collectors.toList()));
|
||||||
|
batch.setConsistencyLevel(sessionOps.getDefaultConsistencyLevel());
|
||||||
|
return batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchOperation logged() {
|
||||||
|
logged = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchOperation setLogged(boolean logStatements) {
|
||||||
|
logged = logStatements;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long sync() {
|
||||||
|
if (operations.size() == 0) return 0L;
|
||||||
|
final Timer.Context context = requestLatency.time();
|
||||||
|
try {
|
||||||
|
batch.setDefaultTimestamp(timestampGenerator.next());
|
||||||
|
ResultSet resultSet =
|
||||||
|
this.execute(
|
||||||
|
sessionOps, null, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
|
||||||
|
if (!resultSet.wasApplied()) {
|
||||||
|
throw new HelenusException("Failed to apply batch.");
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new HelenusException(e);
|
||||||
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
}
|
||||||
|
return batch.getDefaultTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long sync(UnitOfWork uow) {
|
||||||
|
if (operations.size() == 0) return 0L;
|
||||||
|
if (uow == null) return sync();
|
||||||
|
|
||||||
|
final Timer.Context context = requestLatency.time();
|
||||||
|
final Stopwatch timer = Stopwatch.createStarted();
|
||||||
|
try {
|
||||||
|
uow.recordCacheAndDatabaseOperationCount(0, 1);
|
||||||
|
batch.setDefaultTimestamp(timestampGenerator.next());
|
||||||
|
ResultSet resultSet =
|
||||||
|
this.execute(
|
||||||
|
sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
|
||||||
|
if (!resultSet.wasApplied()) {
|
||||||
|
throw new HelenusException("Failed to apply batch.");
|
||||||
|
}
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new HelenusException(e);
|
||||||
|
} finally {
|
||||||
|
context.stop();
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
uow.addDatabaseTime("Cassandra", timer);
|
||||||
|
return batch.getDefaultTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAll(BatchOperation batch) {
|
||||||
|
batch.operations.forEach(o -> this.operations.add(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return toString(true); //TODO(gburd): sessionOps.showQueryValues()
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(boolean showValues) {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append("BEGIN ");
|
||||||
|
if (!logged) {
|
||||||
|
s.append("UNLOGGED ");
|
||||||
|
}
|
||||||
|
s.append("BATCH ");
|
||||||
|
|
||||||
|
if (batch.getDefaultTimestamp() > -9223372036854775808L) {
|
||||||
|
s.append("USING TIMESTAMP ").append(String.valueOf(batch.getDefaultTimestamp())).append(" ");
|
||||||
|
}
|
||||||
|
s.append(
|
||||||
|
operations
|
||||||
|
.stream()
|
||||||
|
.map(o -> Operation.queryString(o.buildStatement(showValues), showValues))
|
||||||
|
.collect(Collectors.joining(" ")));
|
||||||
|
s.append(" APPLY BATCH;");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -21,23 +22,27 @@ import com.datastax.driver.core.Statement;
|
||||||
|
|
||||||
public final class BoundOperation<E> extends AbstractOperation<E, BoundOperation<E>> {
|
public final class BoundOperation<E> extends AbstractOperation<E, BoundOperation<E>> {
|
||||||
|
|
||||||
private final BoundStatement boundStatement;
|
private final BoundStatement boundStatement;
|
||||||
private final AbstractOperation<E, ?> delegate;
|
private final AbstractOperation<E, ?> delegate;
|
||||||
|
|
||||||
public BoundOperation(BoundStatement boundStatement, AbstractOperation<E, ?> operation) {
|
public BoundOperation(BoundStatement boundStatement, AbstractOperation<E, ?> operation) {
|
||||||
super(operation.sessionOps);
|
super(operation.sessionOps);
|
||||||
this.boundStatement = boundStatement;
|
this.boundStatement = boundStatement;
|
||||||
this.delegate = operation;
|
this.delegate = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E transform(ResultSet resultSet) {
|
public E transform(ResultSet resultSet) {
|
||||||
return delegate.transform(resultSet);
|
return delegate.transform(resultSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement buildStatement() {
|
public Statement buildStatement(boolean cached) {
|
||||||
return boundStatement;
|
return boundStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return delegate.isSessionCacheable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,31 +16,36 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.BoundStatement;
|
import com.datastax.driver.core.BoundStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.Statement;
|
import com.datastax.driver.core.Statement;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class BoundOptionalOperation<E> extends AbstractOptionalOperation<E, BoundOptionalOperation<E>> {
|
public final class BoundOptionalOperation<E>
|
||||||
|
extends AbstractOptionalOperation<E, BoundOptionalOperation<E>> {
|
||||||
|
|
||||||
private final BoundStatement boundStatement;
|
private final BoundStatement boundStatement;
|
||||||
private final AbstractOptionalOperation<E, ?> delegate;
|
private final AbstractOptionalOperation<E, ?> delegate;
|
||||||
|
|
||||||
public BoundOptionalOperation(BoundStatement boundStatement, AbstractOptionalOperation<E, ?> operation) {
|
public BoundOptionalOperation(
|
||||||
super(operation.sessionOps);
|
BoundStatement boundStatement, AbstractOptionalOperation<E, ?> operation) {
|
||||||
this.boundStatement = boundStatement;
|
super(operation.sessionOps);
|
||||||
this.delegate = operation;
|
this.boundStatement = boundStatement;
|
||||||
}
|
this.delegate = operation;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<E> transform(ResultSet resultSet) {
|
public Optional<E> transform(ResultSet resultSet) {
|
||||||
return delegate.transform(resultSet);
|
return delegate.transform(resultSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement buildStatement() {
|
public Statement buildStatement(boolean cached) {
|
||||||
return boundStatement;
|
return boundStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return delegate.isSessionCacheable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,31 +16,43 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.BoundStatement;
|
import com.datastax.driver.core.BoundStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.Statement;
|
import com.datastax.driver.core.Statement;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
|
||||||
public final class BoundStreamOperation<E> extends AbstractStreamOperation<E, BoundStreamOperation<E>> {
|
public final class BoundStreamOperation<E>
|
||||||
|
extends AbstractStreamOperation<E, BoundStreamOperation<E>> {
|
||||||
|
|
||||||
private final BoundStatement boundStatement;
|
private final BoundStatement boundStatement;
|
||||||
private final AbstractStreamOperation<E, ?> delegate;
|
private final AbstractStreamOperation<E, ?> delegate;
|
||||||
|
|
||||||
public BoundStreamOperation(BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) {
|
public BoundStreamOperation(
|
||||||
super(operation.sessionOps);
|
BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) {
|
||||||
this.boundStatement = boundStatement;
|
super(operation.sessionOps);
|
||||||
this.delegate = operation;
|
this.boundStatement = boundStatement;
|
||||||
}
|
this.delegate = operation;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<E> transform(ResultSet resultSet) {
|
public List<Facet> bindFacetValues() {
|
||||||
return delegate.transform(resultSet);
|
return delegate.bindFacetValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement buildStatement() {
|
public Stream<E> transform(ResultSet resultSet) {
|
||||||
return boundStatement;
|
return delegate.transform(resultSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement buildStatement(boolean cached) {
|
||||||
|
return boundStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return delegate.isSessionCacheable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,7 +21,6 @@ import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
import com.datastax.driver.core.querybuilder.Select;
|
import com.datastax.driver.core.querybuilder.Select;
|
||||||
import com.datastax.driver.core.querybuilder.Select.Where;
|
import com.datastax.driver.core.querybuilder.Select.Where;
|
||||||
|
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
import net.helenus.core.Filter;
|
import net.helenus.core.Filter;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
|
@ -29,54 +29,57 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class CountOperation extends AbstractFilterOperation<Long, CountOperation> {
|
public final class CountOperation extends AbstractFilterOperation<Long, CountOperation> {
|
||||||
|
|
||||||
private HelenusEntity entity;
|
private HelenusEntity entity;
|
||||||
|
|
||||||
public CountOperation(AbstractSessionOperations sessionOperations) {
|
public CountOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CountOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
|
public CountOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
}
|
//TODO(gburd): cache SELECT COUNT results within the scope of a UOW
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BuiltStatement buildStatement() {
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
|
|
||||||
if (filters != null && !filters.isEmpty()) {
|
if (filters != null && !filters.isEmpty()) {
|
||||||
filters.forEach(f -> addPropertyNode(f.getNode()));
|
filters.forEach(f -> addPropertyNode(f.getNode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
throw new HelenusMappingException("unknown entity");
|
throw new HelenusMappingException("unknown entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
Select select = QueryBuilder.select().countAll().from(entity.getName().toCql());
|
Select select = QueryBuilder.select().countAll().from(entity.getName().toCql());
|
||||||
|
|
||||||
if (filters != null && !filters.isEmpty()) {
|
if (filters != null && !filters.isEmpty()) {
|
||||||
|
|
||||||
Where where = select.where();
|
Where where = select.where();
|
||||||
|
|
||||||
for (Filter<?> filter : filters) {
|
for (Filter<?> filter : filters) {
|
||||||
where.and(filter.getClause(sessionOps.getValuePreparer()));
|
where.and(filter.getClause(sessionOps.getValuePreparer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return select;
|
return select;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long transform(ResultSet resultSet) {
|
public Long transform(ResultSet resultSet) {
|
||||||
return resultSet.one().getLong(0);
|
return resultSet.one().getLong(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPropertyNode(HelenusPropertyNode p) {
|
|
||||||
if (entity == null) {
|
|
||||||
entity = p.getEntity();
|
|
||||||
} else if (entity != p.getEntity()) {
|
|
||||||
throw new HelenusMappingException("you can count columns only in single entity "
|
|
||||||
+ entity.getMappingInterface() + " or " + p.getEntity().getMappingInterface());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private void addPropertyNode(HelenusPropertyNode p) {
|
||||||
|
if (entity == null) {
|
||||||
|
entity = p.getEntity();
|
||||||
|
} else if (entity != p.getEntity()) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"you can count columns only in single entity "
|
||||||
|
+ entity.getMappingInterface()
|
||||||
|
+ " or "
|
||||||
|
+ p.getEntity().getMappingInterface());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,106 +21,155 @@ import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.datastax.driver.core.querybuilder.Delete;
|
import com.datastax.driver.core.querybuilder.Delete;
|
||||||
import com.datastax.driver.core.querybuilder.Delete.Where;
|
import com.datastax.driver.core.querybuilder.Delete.Where;
|
||||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
import net.helenus.core.Filter;
|
import net.helenus.core.Filter;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class DeleteOperation extends AbstractFilterOperation<ResultSet, DeleteOperation> {
|
public final class DeleteOperation extends AbstractFilterOperation<ResultSet, DeleteOperation> {
|
||||||
|
|
||||||
private HelenusEntity entity;
|
private HelenusEntity entity;
|
||||||
|
|
||||||
private boolean ifExists = false;
|
private boolean ifExists = false;
|
||||||
|
|
||||||
private int[] ttl;
|
private int[] ttl;
|
||||||
private long[] timestamp;
|
private long[] timestamp;
|
||||||
|
|
||||||
public DeleteOperation(AbstractSessionOperations sessionOperations) {
|
public DeleteOperation(AbstractSessionOperations sessionOperations) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
|
public DeleteOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
|
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BuiltStatement buildStatement() {
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
|
|
||||||
if (filters != null && !filters.isEmpty()) {
|
if (filters != null && !filters.isEmpty()) {
|
||||||
filters.forEach(f -> addPropertyNode(f.getNode()));
|
filters.forEach(f -> addPropertyNode(f.getNode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
throw new HelenusMappingException("unknown entity");
|
throw new HelenusMappingException("unknown entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters != null && !filters.isEmpty()) {
|
if (filters != null && !filters.isEmpty()) {
|
||||||
|
|
||||||
Delete delete = QueryBuilder.delete().from(entity.getName().toCql());
|
Delete delete = QueryBuilder.delete().from(entity.getName().toCql());
|
||||||
|
|
||||||
if (this.ifExists) {
|
if (this.ifExists) {
|
||||||
delete.ifExists();
|
delete.ifExists();
|
||||||
}
|
}
|
||||||
|
|
||||||
Where where = delete.where();
|
Where where = delete.where();
|
||||||
|
|
||||||
for (Filter<?> filter : filters) {
|
for (Filter<?> filter : filters) {
|
||||||
where.and(filter.getClause(sessionOps.getValuePreparer()));
|
where.and(filter.getClause(sessionOps.getValuePreparer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ifFilters != null && !ifFilters.isEmpty()) {
|
if (ifFilters != null && !ifFilters.isEmpty()) {
|
||||||
|
|
||||||
for (Filter<?> filter : ifFilters) {
|
for (Filter<?> filter : ifFilters) {
|
||||||
delete.onlyIf(filter.getClause(sessionOps.getValuePreparer()));
|
delete.onlyIf(filter.getClause(sessionOps.getValuePreparer()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.ttl != null) {
|
if (this.ttl != null) {
|
||||||
delete.using(QueryBuilder.ttl(this.ttl[0]));
|
delete.using(QueryBuilder.ttl(this.ttl[0]));
|
||||||
}
|
}
|
||||||
if (this.timestamp != null) {
|
if (this.timestamp != null) {
|
||||||
delete.using(QueryBuilder.timestamp(this.timestamp[0]));
|
delete.using(QueryBuilder.timestamp(this.timestamp[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return delete;
|
return delete;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return QueryBuilder.truncate(entity.getName().toCql());
|
return QueryBuilder.truncate(entity.getName().toCql());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResultSet transform(ResultSet resultSet) {
|
public ResultSet transform(ResultSet resultSet) {
|
||||||
return resultSet;
|
return resultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteOperation ifExists() {
|
public DeleteOperation ifExists() {
|
||||||
this.ifExists = true;
|
this.ifExists = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteOperation usingTtl(int ttl) {
|
public DeleteOperation usingTtl(int ttl) {
|
||||||
this.ttl = new int[1];
|
this.ttl = new int[1];
|
||||||
this.ttl[0] = ttl;
|
this.ttl[0] = ttl;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteOperation usingTimestamp(long timestamp) {
|
public DeleteOperation usingTimestamp(long timestamp) {
|
||||||
this.timestamp = new long[1];
|
this.timestamp = new long[1];
|
||||||
this.timestamp[0] = timestamp;
|
this.timestamp[0] = timestamp;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPropertyNode(HelenusPropertyNode p) {
|
private void addPropertyNode(HelenusPropertyNode p) {
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
entity = p.getEntity();
|
entity = p.getEntity();
|
||||||
} else if (entity != p.getEntity()) {
|
} else if (entity != p.getEntity()) {
|
||||||
throw new HelenusMappingException("you can delete rows only in single entity "
|
throw new HelenusMappingException(
|
||||||
+ entity.getMappingInterface() + " or " + p.getEntity().getMappingInterface());
|
"you can delete rows only in single entity "
|
||||||
}
|
+ entity.getMappingInterface()
|
||||||
}
|
+ " or "
|
||||||
|
+ p.getEntity().getMappingInterface());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Facet> bindFacetValues() {
|
||||||
|
return bindFacetValues(getFacets());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isIdempotentOperation() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSet sync() throws TimeoutException {
|
||||||
|
ResultSet result = super.sync();
|
||||||
|
if (entity.isCacheable()) {
|
||||||
|
sessionOps.cacheEvict(bindFacetValues());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSet sync(UnitOfWork uow) throws TimeoutException {
|
||||||
|
if (uow == null) {
|
||||||
|
return sync();
|
||||||
|
}
|
||||||
|
ResultSet result = super.sync(uow);
|
||||||
|
uow.cacheEvict(bindFacetValues());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultSet batch(UnitOfWork uow) throws TimeoutException {
|
||||||
|
if (uow == null) {
|
||||||
|
throw new HelenusException("UnitOfWork cannot be null when batching operations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
uow.cacheEvict(bindFacetValues());
|
||||||
|
uow.batch(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Facet> getFacets() {
|
||||||
|
return entity.getFacets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,154 +16,409 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.datastax.driver.core.querybuilder.Insert;
|
import com.datastax.driver.core.querybuilder.Insert;
|
||||||
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
|
import java.util.*;
|
||||||
import com.google.common.base.Predicates;
|
import java.util.concurrent.TimeoutException;
|
||||||
import com.google.common.collect.Lists;
|
import java.util.function.Function;
|
||||||
import com.google.common.collect.Maps;
|
import java.util.stream.Collectors;
|
||||||
import net.helenus.core.AbstractSessionOperations;
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
import net.helenus.core.Getter;
|
import net.helenus.core.Getter;
|
||||||
|
import net.helenus.core.Helenus;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.core.cache.CacheUtil;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.core.cache.UnboundFacet;
|
||||||
|
import net.helenus.core.reflect.DefaultPrimitiveTypes;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.core.reflect.MapExportable;
|
import net.helenus.core.reflect.MapExportable;
|
||||||
import net.helenus.mapping.ColumnType;
|
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
import net.helenus.mapping.MappingUtil;
|
import net.helenus.mapping.MappingUtil;
|
||||||
import net.helenus.mapping.value.BeanColumnValueProvider;
|
import net.helenus.mapping.value.BeanColumnValueProvider;
|
||||||
import net.helenus.support.Fun;
|
import net.helenus.support.Fun;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class InsertOperation extends AbstractOperation<ResultSet, InsertOperation> {
|
public final class InsertOperation<T> extends AbstractOperation<T, InsertOperation<T>> {
|
||||||
|
|
||||||
private HelenusEntity entity;
|
private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values =
|
||||||
|
new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>();
|
||||||
|
private final T pojo;
|
||||||
|
private final Class<?> resultType;
|
||||||
|
private final Set<String> readSet;
|
||||||
|
private HelenusEntity entity;
|
||||||
|
private boolean ifNotExists;
|
||||||
|
|
||||||
private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values = new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>();
|
private int[] ttl;
|
||||||
private boolean ifNotExists;
|
private long[] timestamp;
|
||||||
|
private long writeTime = 0L;
|
||||||
|
|
||||||
private int[] ttl;
|
public InsertOperation(AbstractSessionOperations sessionOperations, boolean ifNotExists) {
|
||||||
private long[] timestamp;
|
super(sessionOperations);
|
||||||
|
|
||||||
public InsertOperation(AbstractSessionOperations sessionOperations, boolean ifNotExists) {
|
this.pojo = null;
|
||||||
super(sessionOperations);
|
this.readSet = null;
|
||||||
|
this.ifNotExists = ifNotExists;
|
||||||
|
this.resultType = ResultSet.class;
|
||||||
|
}
|
||||||
|
|
||||||
this.ifNotExists = ifNotExists;
|
public InsertOperation(
|
||||||
}
|
AbstractSessionOperations sessionOperations,
|
||||||
|
HelenusEntity entity,
|
||||||
|
Class<?> resultType,
|
||||||
|
boolean ifNotExists) {
|
||||||
|
super(sessionOperations);
|
||||||
|
|
||||||
public InsertOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity, Object pojo,
|
this.pojo = null;
|
||||||
boolean ifNotExists) {
|
this.readSet = null;
|
||||||
super(sessionOperations);
|
this.ifNotExists = ifNotExists;
|
||||||
|
this.resultType = resultType;
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
this.entity = entity;
|
public InsertOperation(
|
||||||
this.ifNotExists = ifNotExists;
|
AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) {
|
||||||
Set<String> keys = (pojo instanceof MapExportable) ? ((MapExportable)pojo).toMap().keySet() : null;
|
super(sessionOperations);
|
||||||
Collection<HelenusProperty> properties = entity.getOrderedProperties();
|
|
||||||
|
|
||||||
for (HelenusProperty prop : properties) {
|
this.pojo = null;
|
||||||
|
this.readSet = null;
|
||||||
|
this.ifNotExists = ifNotExists;
|
||||||
|
this.resultType = resultType;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip properties that are not in the map of the pojo we're storing. This creates a path
|
public InsertOperation(
|
||||||
// for entity instances to be {in,up}serted without including all columns in the INSERT statement.
|
AbstractSessionOperations sessionOperations,
|
||||||
if (keys == null || keys.contains(prop.getPropertyName())) {
|
HelenusEntity entity,
|
||||||
|
T pojo,
|
||||||
|
Set<String> mutations,
|
||||||
|
Set<String> read,
|
||||||
|
boolean ifNotExists) {
|
||||||
|
super(sessionOperations);
|
||||||
|
|
||||||
Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
|
this.pojo = pojo;
|
||||||
value = sessionOps.getValuePreparer().prepareColumnValue(value, prop);
|
this.readSet = read;
|
||||||
|
this.entity = entity;
|
||||||
|
this.ifNotExists = ifNotExists;
|
||||||
|
this.resultType = entity.getMappingInterface();
|
||||||
|
|
||||||
if (value != null) {
|
Collection<HelenusProperty> properties = entity.getOrderedProperties();
|
||||||
HelenusPropertyNode node = new HelenusPropertyNode(prop, Optional.empty());
|
Set<String> keys = (mutations == null) ? null : mutations;
|
||||||
values.add(Fun.Tuple2.of(node, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (HelenusProperty prop : properties) {
|
||||||
|
boolean addProp = false;
|
||||||
|
|
||||||
|
switch (prop.getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
addProp = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
addProp = (keys == null || keys.contains(prop.getPropertyName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addProp) {
|
||||||
|
Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
|
||||||
|
value = sessionOps.getValuePreparer().prepareColumnValue(value, prop);
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
HelenusPropertyNode node = new HelenusPropertyNode(prop, Optional.empty());
|
||||||
|
values.add(Fun.Tuple2.of(node, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InsertOperation<T> ifNotExists() {
|
||||||
|
this.ifNotExists = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InsertOperation<T> ifNotExists(boolean enable) {
|
||||||
|
this.ifNotExists = enable;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> InsertOperation<T> value(Getter<V> getter, V val) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(getter, "getter is empty");
|
||||||
|
|
||||||
|
if (val != null) {
|
||||||
|
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
||||||
|
Object value = sessionOps.getValuePreparer().prepareColumnValue(val, node.getProperty());
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
values.add(Fun.Tuple2.of(node, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
|
List<HelenusEntity> entities =
|
||||||
|
values
|
||||||
|
.stream()
|
||||||
|
.map(t -> t._1.getProperty().getEntity())
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (entities.size() != 1) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"you can insert only single entity at a time, found: "
|
||||||
|
+ entities
|
||||||
|
.stream()
|
||||||
|
.map(e -> e.getMappingInterface().toString())
|
||||||
|
.collect(Collectors.joining(", ")));
|
||||||
|
}
|
||||||
|
HelenusEntity entity = entities.get(0);
|
||||||
|
if (this.entity != null) {
|
||||||
|
if (this.entity != entity) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"you can insert only single entity at a time, found: "
|
||||||
|
+ this.entity.getMappingInterface().toString()
|
||||||
|
+ ", "
|
||||||
|
+ entity.getMappingInterface().toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.isEmpty()) return null;
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
throw new HelenusMappingException("unknown entity");
|
||||||
|
}
|
||||||
|
|
||||||
|
Insert insert = QueryBuilder.insertInto(entity.getName().toCql());
|
||||||
|
|
||||||
|
if (ifNotExists) {
|
||||||
|
insert.ifNotExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
values.forEach(
|
||||||
|
t -> {
|
||||||
|
insert.value(t._1.getColumnName(), t._2);
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO(gburd): IF NOT EXISTS when @Constraints.Relationship is 1:1 or 1:m
|
||||||
|
|
||||||
|
if (this.ttl != null) {
|
||||||
|
insert.using(QueryBuilder.ttl(this.ttl[0]));
|
||||||
|
}
|
||||||
|
if (this.timestamp != null) {
|
||||||
|
insert.using(QueryBuilder.timestamp(this.timestamp[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return insert;
|
||||||
|
}
|
||||||
|
|
||||||
|
private T newInstance(Class<?> iface) {
|
||||||
|
if (values.size() > 0) {
|
||||||
|
boolean immutable = entity.isDraftable();
|
||||||
|
Collection<HelenusProperty> properties = entity.getOrderedProperties();
|
||||||
|
Map<String, Object> backingMap = new HashMap<String, Object>(properties.size());
|
||||||
|
|
||||||
|
// First, add all the inserted values into our new map.
|
||||||
|
values.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2));
|
||||||
|
|
||||||
|
// Then, fill in all the rest of the properties.
|
||||||
|
for (HelenusProperty prop : properties) {
|
||||||
|
String key = prop.getPropertyName();
|
||||||
|
if (backingMap.containsKey(key)) {
|
||||||
|
// Some values man need to be converted (e.g. from String to Enum). This is done
|
||||||
|
// within the BeanColumnValueProvider below.
|
||||||
|
Optional<Function<Object, Object>> converter =
|
||||||
|
prop.getReadConverter(sessionOps.getSessionRepository());
|
||||||
|
if (converter.isPresent()) {
|
||||||
|
backingMap.put(key, converter.get().apply(backingMap.get(key)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we started this operation with an instance of this type, use values from
|
||||||
|
// that.
|
||||||
|
if (pojo != null) {
|
||||||
|
backingMap.put(
|
||||||
|
key, BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, immutable));
|
||||||
|
} else {
|
||||||
|
// Otherwise we'll use default values for the property type if available.
|
||||||
|
Class<?> propType = prop.getJavaType();
|
||||||
|
if (propType.isPrimitive()) {
|
||||||
|
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType);
|
||||||
|
if (type == null) {
|
||||||
|
throw new HelenusException("unknown primitive type " + propType);
|
||||||
|
}
|
||||||
|
backingMap.put(key, type.getDefaultValue());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// Lastly, create a new proxy object for the entity and return the new instance.
|
||||||
|
return (T) Helenus.map(iface, backingMap);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public T transform(ResultSet resultSet) {
|
||||||
|
if ((ifNotExists == true) && (resultSet.wasApplied() == false)) {
|
||||||
|
throw new HelenusException("Statement was not applied due to consistency constraints");
|
||||||
|
}
|
||||||
|
|
||||||
public InsertOperation ifNotExists() {
|
Class<?> iface = entity.getMappingInterface();
|
||||||
this.ifNotExists = true;
|
if (resultType == iface) {
|
||||||
return this;
|
T o = newInstance(iface);
|
||||||
}
|
if (o == null) {
|
||||||
|
// Oddly, this insert didn't change anything so simply return the pojo.
|
||||||
|
return (T) pojo;
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
return (T) resultSet;
|
||||||
|
}
|
||||||
|
|
||||||
public InsertOperation ifNotExists(boolean enable) {
|
public InsertOperation<T> usingTtl(int ttl) {
|
||||||
this.ifNotExists = enable;
|
this.ttl = new int[1];
|
||||||
return this;
|
this.ttl[0] = ttl;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public <V> InsertOperation value(Getter<V> getter, V val) {
|
public InsertOperation<T> usingTimestamp(long timestamp) {
|
||||||
|
this.timestamp = new long[1];
|
||||||
|
this.timestamp[0] = timestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(getter, "getter is empty");
|
protected void adjustTtlAndWriteTime(MapExportable pojo) {
|
||||||
|
if (ttl != null || writeTime != 0L) {
|
||||||
|
List<String> columnNames =
|
||||||
|
values
|
||||||
|
.stream()
|
||||||
|
.map(t -> t._1.getProperty())
|
||||||
|
.filter(
|
||||||
|
prop -> {
|
||||||
|
switch (prop.getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(prop -> prop.getColumnName().toCql(false))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (val != null) {
|
if (columnNames.size() > 0) {
|
||||||
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
|
if (ttl != null) {
|
||||||
Object value = sessionOps.getValuePreparer().prepareColumnValue(val, node.getProperty());
|
columnNames.forEach(name -> pojo.put(CacheUtil.ttlKey(name), ttl));
|
||||||
|
}
|
||||||
|
if (writeTime != 0L) {
|
||||||
|
columnNames.forEach(name -> pojo.put(CacheUtil.writeTimeKey(name), writeTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (value != null) {
|
@Override
|
||||||
values.add(Fun.Tuple2.of(node, value));
|
protected boolean isIdempotentOperation() {
|
||||||
}
|
return values.stream().map(v -> v._1.getProperty()).allMatch(prop -> prop.isIdempotent())
|
||||||
}
|
|| super.isIdempotentOperation();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
@Override
|
||||||
}
|
public T sync() throws TimeoutException {
|
||||||
|
T result = super.sync();
|
||||||
|
if (entity.isCacheable() && result != null) {
|
||||||
|
adjustTtlAndWriteTime((MapExportable) result);
|
||||||
|
sessionOps.updateCache(result, bindFacetValues());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BuiltStatement buildStatement() {
|
public T sync(UnitOfWork uow) throws TimeoutException {
|
||||||
|
if (uow == null) {
|
||||||
|
return sync();
|
||||||
|
}
|
||||||
|
T result = super.sync(uow);
|
||||||
|
if (result != null && pojo != null && !(pojo == result) && pojo.equals(result)) {
|
||||||
|
// To preserve object identity we need to find this object in cache
|
||||||
|
// because it was unchanged by the INSERT but pojo in this case was
|
||||||
|
// the result of a draft.build().
|
||||||
|
T cachedValue = (T) uow.cacheLookup(bindFacetValues());
|
||||||
|
if (cachedValue != null) {
|
||||||
|
result = cachedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Class<?> iface = entity.getMappingInterface();
|
||||||
|
if (resultType == iface) {
|
||||||
|
if (entity != null && MapExportable.class.isAssignableFrom(entity.getMappingInterface())) {
|
||||||
|
adjustTtlAndWriteTime((MapExportable) result);
|
||||||
|
}
|
||||||
|
cacheUpdate(uow, result, bindFacetValues());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
values.forEach(t -> addPropertyNode(t._1));
|
public T batch(UnitOfWork uow) throws TimeoutException {
|
||||||
|
if (uow == null) {
|
||||||
|
throw new HelenusException("UnitOfWork cannot be null when batching operations.");
|
||||||
|
}
|
||||||
|
|
||||||
if (values.isEmpty())
|
if (this.entity != null) {
|
||||||
return null;
|
Class<?> iface = this.entity.getMappingInterface();
|
||||||
|
if (resultType == iface) {
|
||||||
|
final T result = (pojo == null) ? newInstance(iface) : pojo;
|
||||||
|
if (result != null) {
|
||||||
|
adjustTtlAndWriteTime((MapExportable) result);
|
||||||
|
cacheUpdate(uow, result, bindFacetValues());
|
||||||
|
}
|
||||||
|
uow.batch(this);
|
||||||
|
return (T) result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (entity == null) {
|
return sync(uow);
|
||||||
throw new HelenusMappingException("unknown entity");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Insert insert = QueryBuilder.insertInto(entity.getName().toCql());
|
@Override
|
||||||
|
public List<Facet> bindFacetValues() {
|
||||||
|
List<Facet> facets = getFacets();
|
||||||
|
if (facets == null || facets.size() == 0) {
|
||||||
|
return new ArrayList<Facet>();
|
||||||
|
}
|
||||||
|
List<Facet> boundFacets = new ArrayList<>();
|
||||||
|
Map<HelenusProperty, Object> valuesMap = new HashMap<>(values.size());
|
||||||
|
values.forEach(t -> valuesMap.put(t._1.getProperty(), t._2));
|
||||||
|
|
||||||
if (ifNotExists) {
|
for (Facet facet : facets) {
|
||||||
insert.ifNotExists();
|
if (facet instanceof UnboundFacet) {
|
||||||
}
|
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||||
|
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||||
|
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||||
|
Object value = valuesMap.get(prop);
|
||||||
|
if (value != null) {
|
||||||
|
binder.setValueForProperty(prop, value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (binder.isBound()) {
|
||||||
|
boundFacets.add(binder.bind());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boundFacets.add(facet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boundFacets;
|
||||||
|
}
|
||||||
|
|
||||||
values.forEach(t -> {
|
@Override
|
||||||
insert.value(t._1.getColumnName(), t._2);
|
public List<Facet> getFacets() {
|
||||||
});
|
if (entity != null) {
|
||||||
|
return entity.getFacets();
|
||||||
if (this.ttl != null) {
|
} else {
|
||||||
insert.using(QueryBuilder.ttl(this.ttl[0]));
|
return new ArrayList<Facet>();
|
||||||
}
|
}
|
||||||
if (this.timestamp != null) {
|
}
|
||||||
insert.using(QueryBuilder.timestamp(this.timestamp[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return insert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultSet transform(ResultSet resultSet) {
|
|
||||||
return resultSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InsertOperation usingTtl(int ttl) {
|
|
||||||
this.ttl = new int[1];
|
|
||||||
this.ttl[0] = ttl;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InsertOperation usingTimestamp(long timestamp) {
|
|
||||||
this.timestamp = new long[1];
|
|
||||||
this.timestamp[0] = timestamp;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPropertyNode(HelenusPropertyNode p) {
|
|
||||||
if (entity == null) {
|
|
||||||
entity = p.getEntity();
|
|
||||||
} else if (entity != p.getEntity()) {
|
|
||||||
throw new HelenusMappingException("you can insert only single entity " + entity.getMappingInterface()
|
|
||||||
+ " or " + p.getEntity().getMappingInterface());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
211
src/main/java/net/helenus/core/operation/Operation.java
Normal file
211
src/main/java/net/helenus/core/operation/Operation.java
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.operation;
|
||||||
|
|
||||||
|
import com.codahale.metrics.Meter;
|
||||||
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
import com.codahale.metrics.Timer;
|
||||||
|
import com.datastax.driver.core.*;
|
||||||
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
|
import com.google.common.base.Stopwatch;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import net.helenus.core.AbstractSessionOperations;
|
||||||
|
import net.helenus.core.UnitOfWork;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.support.HelenusException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public abstract class Operation<E> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Operation.class);
|
||||||
|
|
||||||
|
protected final AbstractSessionOperations sessionOps;
|
||||||
|
protected boolean showValues;
|
||||||
|
protected long queryExecutionTimeout = 10;
|
||||||
|
protected TimeUnit queryTimeoutUnits = TimeUnit.SECONDS;
|
||||||
|
protected final Meter uowCacheHits;
|
||||||
|
protected final Meter uowCacheMiss;
|
||||||
|
protected final Meter sessionCacheHits;
|
||||||
|
protected final Meter sessionCacheMiss;
|
||||||
|
protected final Meter cacheHits;
|
||||||
|
protected final Meter cacheMiss;
|
||||||
|
protected final Timer requestLatency;
|
||||||
|
|
||||||
|
Operation(AbstractSessionOperations sessionOperations) {
|
||||||
|
this.sessionOps = sessionOperations;
|
||||||
|
this.showValues = sessionOps.showValues();
|
||||||
|
MetricRegistry metrics = sessionOperations.getMetricRegistry();
|
||||||
|
if (metrics == null) {
|
||||||
|
metrics = new MetricRegistry();
|
||||||
|
}
|
||||||
|
this.uowCacheHits = metrics.meter("net.helenus.UOW-cache-hits");
|
||||||
|
this.uowCacheMiss = metrics.meter("net.helenus.UOW-cache-miss");
|
||||||
|
this.sessionCacheHits = metrics.meter("net.helenus.session-cache-hits");
|
||||||
|
this.sessionCacheMiss = metrics.meter("net.helenus.session-cache-miss");
|
||||||
|
this.cacheHits = metrics.meter("net.helenus.cache-hits");
|
||||||
|
this.cacheMiss = metrics.meter("net.helenus.cache-miss");
|
||||||
|
this.requestLatency = metrics.timer("net.helenus.request-latency");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String queryString(BatchOperation operation, boolean includeValues) {
|
||||||
|
return operation.toString(includeValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String queryString(Statement statement, boolean includeValues) {
|
||||||
|
String query = null;
|
||||||
|
if (statement instanceof BuiltStatement) {
|
||||||
|
BuiltStatement builtStatement = (BuiltStatement) statement;
|
||||||
|
if (includeValues) {
|
||||||
|
RegularStatement regularStatement = builtStatement.setForceNoValues(true);
|
||||||
|
query = regularStatement.getQueryString();
|
||||||
|
} else {
|
||||||
|
query = builtStatement.getQueryString();
|
||||||
|
}
|
||||||
|
} else if (statement instanceof RegularStatement) {
|
||||||
|
RegularStatement regularStatement = (RegularStatement) statement;
|
||||||
|
query = regularStatement.getQueryString();
|
||||||
|
} else {
|
||||||
|
query = statement.toString();
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultSet execute(
|
||||||
|
AbstractSessionOperations session,
|
||||||
|
UnitOfWork uow,
|
||||||
|
long timeout,
|
||||||
|
TimeUnit units,
|
||||||
|
boolean showValues,
|
||||||
|
boolean cached)
|
||||||
|
throws TimeoutException {
|
||||||
|
|
||||||
|
Statement statement = options(buildStatement(cached));
|
||||||
|
|
||||||
|
if (session.isShowCql()) {
|
||||||
|
String stmt =
|
||||||
|
(this instanceof BatchOperation)
|
||||||
|
? queryString((BatchOperation) this, showValues)
|
||||||
|
: queryString(statement, showValues);
|
||||||
|
session.getPrintStream().println(stmt);
|
||||||
|
} else if (LOG.isDebugEnabled()) {
|
||||||
|
String stmt =
|
||||||
|
(this instanceof BatchOperation)
|
||||||
|
? queryString((BatchOperation) this, showValues)
|
||||||
|
: queryString(statement, showValues);
|
||||||
|
LOG.info("CQL> " + stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stopwatch timer = Stopwatch.createStarted();
|
||||||
|
try {
|
||||||
|
ResultSetFuture futureResultSet = session.executeAsync(statement, uow, timer);
|
||||||
|
if (uow != null) uow.recordCacheAndDatabaseOperationCount(0, 1);
|
||||||
|
ResultSet resultSet = futureResultSet.getUninterruptibly(timeout, units);
|
||||||
|
ColumnDefinitions columnDefinitions = resultSet.getColumnDefinitions();
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
ExecutionInfo ei = resultSet.getExecutionInfo();
|
||||||
|
Host qh = ei.getQueriedHost();
|
||||||
|
String oh =
|
||||||
|
ei.getTriedHosts()
|
||||||
|
.stream()
|
||||||
|
.map(Host::getAddress)
|
||||||
|
.map(InetAddress::toString)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
ConsistencyLevel cl = ei.getAchievedConsistencyLevel();
|
||||||
|
if (cl == null) {
|
||||||
|
cl = statement.getConsistencyLevel();
|
||||||
|
}
|
||||||
|
int se = ei.getSpeculativeExecutions();
|
||||||
|
String warn = ei.getWarnings().stream().collect(Collectors.joining(", "));
|
||||||
|
String ri =
|
||||||
|
String.format(
|
||||||
|
"%s %s ~%s %s %s%s%sspec-retries: %d",
|
||||||
|
"server v" + qh.getCassandraVersion(),
|
||||||
|
qh.getAddress().toString(),
|
||||||
|
(oh != null && !oh.equals("")) ? " [tried: " + oh + "]" : "",
|
||||||
|
qh.getDatacenter(),
|
||||||
|
qh.getRack(),
|
||||||
|
(cl != null)
|
||||||
|
? (" consistency: "
|
||||||
|
+ cl.name()
|
||||||
|
+ " "
|
||||||
|
+ (cl.isDCLocal() ? " DC " : "")
|
||||||
|
+ (cl.isSerial() ? " SC " : ""))
|
||||||
|
: "",
|
||||||
|
(warn != null && !warn.equals("")) ? ": " + warn : "",
|
||||||
|
se);
|
||||||
|
if (uow != null) uow.setInfo(ri);
|
||||||
|
else LOG.debug(ri);
|
||||||
|
}
|
||||||
|
if (!resultSet.wasApplied()
|
||||||
|
&& !(columnDefinitions.size() > 1 || !columnDefinitions.contains("[applied]"))) {
|
||||||
|
throw new HelenusException("Operation Failed");
|
||||||
|
}
|
||||||
|
return resultSet;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
timer.stop();
|
||||||
|
if (uow != null) uow.addDatabaseTime("Cassandra", timer);
|
||||||
|
log(statement, uow, timer, showValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(Statement statement, UnitOfWork uow, Stopwatch timer, boolean showValues) {
|
||||||
|
if (LOG.isInfoEnabled()) {
|
||||||
|
String uowString = "";
|
||||||
|
if (uow != null) {
|
||||||
|
uowString = "UOW(" + uow.hashCode() + ")";
|
||||||
|
}
|
||||||
|
String timerString = "";
|
||||||
|
if (timer != null) {
|
||||||
|
timerString = String.format(" %s ", timer.toString());
|
||||||
|
}
|
||||||
|
LOG.info(
|
||||||
|
String.format(
|
||||||
|
"%s%s%s", uowString, timerString, Operation.queryString(statement, showValues)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isIdempotentOperation() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Statement options(Statement statement) {
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Statement buildStatement(boolean cached) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Facet> getFacets() {
|
||||||
|
return new ArrayList<Facet>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Facet> bindFacetValues() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,28 +21,27 @@ import com.datastax.driver.core.PreparedStatement;
|
||||||
|
|
||||||
public final class PreparedOperation<E> {
|
public final class PreparedOperation<E> {
|
||||||
|
|
||||||
private final PreparedStatement preparedStatement;
|
private final PreparedStatement preparedStatement;
|
||||||
private final AbstractOperation<E, ?> operation;
|
private final AbstractOperation<E, ?> operation;
|
||||||
|
|
||||||
public PreparedOperation(PreparedStatement statement, AbstractOperation<E, ?> operation) {
|
public PreparedOperation(PreparedStatement statement, AbstractOperation<E, ?> operation) {
|
||||||
this.preparedStatement = statement;
|
this.preparedStatement = statement;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PreparedStatement getPreparedStatement() {
|
public PreparedStatement getPreparedStatement() {
|
||||||
return preparedStatement;
|
return preparedStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundOperation<E> bind(Object... params) {
|
public BoundOperation<E> bind(Object... params) {
|
||||||
|
|
||||||
BoundStatement boundStatement = preparedStatement.bind(params);
|
BoundStatement boundStatement = preparedStatement.bind(params);
|
||||||
|
|
||||||
return new BoundOperation<E>(boundStatement, operation);
|
return new BoundOperation<E>(boundStatement, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return preparedStatement.getQueryString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return preparedStatement.getQueryString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,28 +21,28 @@ import com.datastax.driver.core.PreparedStatement;
|
||||||
|
|
||||||
public final class PreparedOptionalOperation<E> {
|
public final class PreparedOptionalOperation<E> {
|
||||||
|
|
||||||
private final PreparedStatement preparedStatement;
|
private final PreparedStatement preparedStatement;
|
||||||
private final AbstractOptionalOperation<E, ?> operation;
|
private final AbstractOptionalOperation<E, ?> operation;
|
||||||
|
|
||||||
public PreparedOptionalOperation(PreparedStatement statement, AbstractOptionalOperation<E, ?> operation) {
|
public PreparedOptionalOperation(
|
||||||
this.preparedStatement = statement;
|
PreparedStatement statement, AbstractOptionalOperation<E, ?> operation) {
|
||||||
this.operation = operation;
|
this.preparedStatement = statement;
|
||||||
}
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
public PreparedStatement getPreparedStatement() {
|
public PreparedStatement getPreparedStatement() {
|
||||||
return preparedStatement;
|
return preparedStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundOptionalOperation<E> bind(Object... params) {
|
public BoundOptionalOperation<E> bind(Object... params) {
|
||||||
|
|
||||||
BoundStatement boundStatement = preparedStatement.bind(params);
|
BoundStatement boundStatement = preparedStatement.bind(params);
|
||||||
|
|
||||||
return new BoundOptionalOperation<E>(boundStatement, operation);
|
return new BoundOptionalOperation<E>(boundStatement, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return preparedStatement.getQueryString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return preparedStatement.getQueryString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,28 +21,26 @@ import com.datastax.driver.core.PreparedStatement;
|
||||||
|
|
||||||
public final class PreparedStreamOperation<E> {
|
public final class PreparedStreamOperation<E> {
|
||||||
|
|
||||||
private final PreparedStatement preparedStatement;
|
private final PreparedStatement preparedStatement;
|
||||||
private final AbstractStreamOperation<E, ?> operation;
|
private final AbstractStreamOperation<E, ?> operation;
|
||||||
|
|
||||||
public PreparedStreamOperation(PreparedStatement statement, AbstractStreamOperation<E, ?> operation) {
|
public PreparedStreamOperation(
|
||||||
this.preparedStatement = statement;
|
PreparedStatement statement, AbstractStreamOperation<E, ?> operation) {
|
||||||
this.operation = operation;
|
this.preparedStatement = statement;
|
||||||
}
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
public PreparedStatement getPreparedStatement() {
|
public PreparedStatement getPreparedStatement() {
|
||||||
return preparedStatement;
|
return preparedStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundStreamOperation<E> bind(Object... params) {
|
public BoundStreamOperation<E> bind(Object... params) {
|
||||||
|
BoundStatement boundStatement = preparedStatement.bind(params);
|
||||||
BoundStatement boundStatement = preparedStatement.bind(params);
|
return new BoundStreamOperation<E>(boundStatement, operation);
|
||||||
|
}
|
||||||
return new BoundStreamOperation<E>(boundStatement, operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return preparedStatement.getQueryString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return preparedStatement.getQueryString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,72 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
*
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* You may obtain a copy of the License at
|
* 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
|
*
|
||||||
*
|
* 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,
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* See the License for the specific language governing permissions and
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* limitations under the License.
|
* See the License for the specific language governing permissions and
|
||||||
*/
|
* limitations under the License.
|
||||||
package net.helenus.core.operation;
|
*/
|
||||||
|
package net.helenus.core.operation;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
import com.datastax.driver.core.ResultSet;
|
||||||
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import java.util.List;
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
public final class SelectFirstOperation<E> extends AbstractFilterOptionalOperation<E, SelectFirstOperation<E>> {
|
import net.helenus.core.cache.Facet;
|
||||||
|
|
||||||
private final SelectOperation<E> src;
|
public final class SelectFirstOperation<E>
|
||||||
|
extends AbstractFilterOptionalOperation<E, SelectFirstOperation<E>> {
|
||||||
public SelectFirstOperation(SelectOperation<E> src) {
|
|
||||||
super(src.sessionOps);
|
private final SelectOperation<E> delegate;
|
||||||
|
|
||||||
this.src = src;
|
public SelectFirstOperation(SelectOperation<E> delegate) {
|
||||||
this.filters = src.filters;
|
super(delegate.sessionOps);
|
||||||
this.ifFilters = src.ifFilters;
|
|
||||||
}
|
this.delegate = delegate;
|
||||||
|
this.filters = delegate.filters;
|
||||||
public <R> SelectFirstTransformingOperation<R, E> map(Function<E, R> fn) {
|
this.ifFilters = delegate.ifFilters;
|
||||||
return new SelectFirstTransformingOperation<R, E>(src, fn);
|
}
|
||||||
}
|
|
||||||
|
public <R> SelectFirstTransformingOperation<R, E> map(Function<E, R> fn) {
|
||||||
@Override
|
return new SelectFirstTransformingOperation<R, E>(delegate, fn);
|
||||||
public BuiltStatement buildStatement() {
|
}
|
||||||
return src.buildStatement();
|
|
||||||
}
|
@Override
|
||||||
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
@Override
|
return delegate.buildStatement(cached);
|
||||||
public Optional<E> transform(ResultSet resultSet) {
|
}
|
||||||
return src.transform(resultSet).findFirst();
|
|
||||||
}
|
@Override
|
||||||
|
public List<Facet> getFacets() {
|
||||||
}
|
return delegate.getFacets();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Facet> bindFacetValues() {
|
||||||
|
return delegate.bindFacetValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<E> transform(ResultSet resultSet) {
|
||||||
|
return delegate.transform(resultSet).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return delegate.isSessionCacheable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean ignoreCache() {
|
||||||
|
return delegate.ignoreCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,50 +1,65 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
*
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* You may obtain a copy of the License at
|
* 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
|
*
|
||||||
*
|
* 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,
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* See the License for the specific language governing permissions and
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* limitations under the License.
|
* See the License for the specific language governing permissions and
|
||||||
*/
|
* limitations under the License.
|
||||||
package net.helenus.core.operation;
|
*/
|
||||||
|
package net.helenus.core.operation;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
import com.datastax.driver.core.ResultSet;
|
||||||
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import java.util.List;
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
public final class SelectFirstTransformingOperation<R, E>
|
import net.helenus.core.cache.Facet;
|
||||||
extends
|
|
||||||
AbstractFilterOptionalOperation<R, SelectFirstTransformingOperation<R, E>> {
|
public final class SelectFirstTransformingOperation<R, E>
|
||||||
|
extends AbstractFilterOptionalOperation<R, SelectFirstTransformingOperation<R, E>> {
|
||||||
private final SelectOperation<E> src;
|
|
||||||
private final Function<E, R> fn;
|
private final SelectOperation<E> delegate;
|
||||||
|
private final Function<E, R> fn;
|
||||||
public SelectFirstTransformingOperation(SelectOperation<E> src, Function<E, R> fn) {
|
|
||||||
super(src.sessionOps);
|
public SelectFirstTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
|
||||||
|
super(delegate.sessionOps);
|
||||||
this.src = src;
|
|
||||||
this.fn = fn;
|
this.delegate = delegate;
|
||||||
this.filters = src.filters;
|
this.fn = fn;
|
||||||
this.ifFilters = src.ifFilters;
|
this.filters = delegate.filters;
|
||||||
}
|
this.ifFilters = delegate.ifFilters;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public BuiltStatement buildStatement() {
|
@Override
|
||||||
return src.buildStatement();
|
public List<Facet> bindFacetValues() {
|
||||||
}
|
return delegate.bindFacetValues();
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public Optional<R> transform(ResultSet resultSet) {
|
@Override
|
||||||
return src.transform(resultSet).findFirst().map(fn);
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
}
|
return delegate.buildStatement(cached);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public Optional<R> transform(ResultSet resultSet) {
|
||||||
|
return delegate.transform(resultSet).findFirst().map(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return delegate.isSessionCacheable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean ignoreCache() {
|
||||||
|
return delegate.ignoreCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,11 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.operation;
|
package net.helenus.core.operation;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.ResultSet;
|
import com.datastax.driver.core.ResultSet;
|
||||||
import com.datastax.driver.core.Row;
|
import com.datastax.driver.core.Row;
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
|
@ -28,231 +24,355 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
|
||||||
import com.datastax.driver.core.querybuilder.Select;
|
import com.datastax.driver.core.querybuilder.Select;
|
||||||
import com.datastax.driver.core.querybuilder.Select.Selection;
|
import com.datastax.driver.core.querybuilder.Select.Selection;
|
||||||
import com.datastax.driver.core.querybuilder.Select.Where;
|
import com.datastax.driver.core.querybuilder.Select.Where;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
import net.helenus.core.*;
|
import net.helenus.core.*;
|
||||||
|
import net.helenus.core.cache.CacheUtil;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.core.cache.UnboundFacet;
|
||||||
|
import net.helenus.core.reflect.Entity;
|
||||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
|
import net.helenus.mapping.HelenusProperty;
|
||||||
import net.helenus.mapping.MappingUtil;
|
import net.helenus.mapping.MappingUtil;
|
||||||
import net.helenus.mapping.OrderingDirection;
|
import net.helenus.mapping.OrderingDirection;
|
||||||
import net.helenus.mapping.value.ColumnValueProvider;
|
import net.helenus.mapping.value.ColumnValueProvider;
|
||||||
import net.helenus.mapping.value.ValueProviderMap;
|
import net.helenus.mapping.value.ValueProviderMap;
|
||||||
import net.helenus.support.Fun;
|
import net.helenus.support.Fun;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, SelectOperation<E>> {
|
public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, SelectOperation<E>> {
|
||||||
|
|
||||||
protected Function<Row, E> rowMapper = null;
|
private static final Logger LOG = LoggerFactory.getLogger(SelectOperation.class);
|
||||||
protected final List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
|
|
||||||
|
|
||||||
protected List<Ordering> ordering = null;
|
protected final List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
|
||||||
protected Integer limit = null;
|
protected Function<Row, E> rowMapper = null;
|
||||||
protected boolean allowFiltering = false;
|
protected List<Ordering> ordering = null;
|
||||||
|
protected Integer limit = null;
|
||||||
|
protected boolean allowFiltering = false;
|
||||||
|
|
||||||
public SelectOperation(AbstractSessionOperations sessionOperations) {
|
protected String alternateTableName = null;
|
||||||
super(sessionOperations);
|
protected boolean isCacheable = false;
|
||||||
|
protected boolean implementsEntityType = false;
|
||||||
|
|
||||||
this.rowMapper = new Function<Row, E>() {
|
@SuppressWarnings("unchecked")
|
||||||
|
public SelectOperation(AbstractSessionOperations sessionOperations) {
|
||||||
|
super(sessionOperations);
|
||||||
|
|
||||||
@Override
|
this.rowMapper =
|
||||||
public E apply(Row source) {
|
new Function<Row, E>() {
|
||||||
|
|
||||||
ColumnValueProvider valueProvider = sessionOps.getValueProvider();
|
@Override
|
||||||
Object[] arr = new Object[props.size()];
|
public E apply(Row source) {
|
||||||
|
|
||||||
int i = 0;
|
ColumnValueProvider valueProvider = sessionOps.getValueProvider();
|
||||||
for (HelenusPropertyNode p : props) {
|
Object[] arr = new Object[props.size()];
|
||||||
Object value = valueProvider.getColumnValue(source, -1, p.getProperty());
|
|
||||||
arr[i++] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (E) Fun.ArrayTuple.of(arr);
|
int i = 0;
|
||||||
}
|
for (HelenusPropertyNode p : props) {
|
||||||
|
Object value = valueProvider.getColumnValue(source, -1, p.getProperty());
|
||||||
|
arr[i++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
return (E) Fun.ArrayTuple.of(arr);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
|
public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
|
||||||
|
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
|
|
||||||
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty()))
|
entity
|
||||||
.forEach(p -> this.props.add(p));
|
.getOrderedProperties()
|
||||||
|
.stream()
|
||||||
|
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
|
||||||
|
.forEach(p -> this.props.add(p));
|
||||||
|
|
||||||
}
|
this.isCacheable = entity.isCacheable();
|
||||||
|
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
|
||||||
|
}
|
||||||
|
|
||||||
public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity,
|
public SelectOperation(
|
||||||
Function<Row, E> rowMapper) {
|
AbstractSessionOperations sessionOperations,
|
||||||
|
HelenusEntity entity,
|
||||||
|
Function<Row, E> rowMapper) {
|
||||||
|
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
this.rowMapper = rowMapper;
|
this.rowMapper = rowMapper;
|
||||||
|
|
||||||
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty()))
|
entity
|
||||||
.forEach(p -> this.props.add(p));
|
.getOrderedProperties()
|
||||||
|
.stream()
|
||||||
|
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
|
||||||
|
.forEach(p -> this.props.add(p));
|
||||||
|
|
||||||
}
|
this.isCacheable = entity.isCacheable();
|
||||||
|
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
|
||||||
|
}
|
||||||
|
|
||||||
public SelectOperation(AbstractSessionOperations sessionOperations, Function<Row, E> rowMapper,
|
public SelectOperation(
|
||||||
HelenusPropertyNode... props) {
|
AbstractSessionOperations sessionOperations,
|
||||||
|
Function<Row, E> rowMapper,
|
||||||
|
HelenusPropertyNode... props) {
|
||||||
|
|
||||||
super(sessionOperations);
|
super(sessionOperations);
|
||||||
this.rowMapper = rowMapper;
|
|
||||||
Collections.addAll(this.props, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CountOperation count() {
|
this.rowMapper = rowMapper;
|
||||||
|
Collections.addAll(this.props, props);
|
||||||
|
|
||||||
HelenusEntity entity = null;
|
HelenusEntity entity = props[0].getEntity();
|
||||||
for (HelenusPropertyNode prop : props) {
|
this.isCacheable = entity.isCacheable();
|
||||||
|
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
|
||||||
|
}
|
||||||
|
|
||||||
if (entity == null) {
|
public CountOperation count() {
|
||||||
entity = prop.getEntity();
|
|
||||||
} else if (entity != prop.getEntity()) {
|
|
||||||
throw new HelenusMappingException("you can count records only from a single entity "
|
|
||||||
+ entity.getMappingInterface() + " or " + prop.getEntity().getMappingInterface());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CountOperation(sessionOps, entity);
|
HelenusEntity entity = null;
|
||||||
}
|
for (HelenusPropertyNode prop : props) {
|
||||||
|
|
||||||
public SelectFirstOperation<E> single() {
|
if (entity == null) {
|
||||||
limit(1);
|
entity = prop.getEntity();
|
||||||
return new SelectFirstOperation<E>(this);
|
} else if (entity != prop.getEntity()) {
|
||||||
}
|
throw new HelenusMappingException(
|
||||||
|
"you can count records only from a single entity "
|
||||||
|
+ entity.getMappingInterface()
|
||||||
|
+ " or "
|
||||||
|
+ prop.getEntity().getMappingInterface());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public <R> SelectTransformingOperation<R, E> mapTo(Class<R> entityClass) {
|
return new CountOperation(sessionOps, entity);
|
||||||
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(entityClass, "entityClass is null");
|
public <V extends E> SelectOperation<E> from(Class<V> materializedViewClass) {
|
||||||
|
Objects.requireNonNull(materializedViewClass);
|
||||||
|
HelenusEntity entity = Helenus.entity(materializedViewClass);
|
||||||
|
this.alternateTableName = entity.getName().toCql();
|
||||||
|
this.props.clear();
|
||||||
|
entity
|
||||||
|
.getOrderedProperties()
|
||||||
|
.stream()
|
||||||
|
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
|
||||||
|
.forEach(p -> this.props.add(p));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
HelenusEntity entity = Helenus.entity(entityClass);
|
public SelectFirstOperation<E> single() {
|
||||||
|
limit(1);
|
||||||
|
return new SelectFirstOperation<E>(this);
|
||||||
|
}
|
||||||
|
|
||||||
this.rowMapper = null;
|
public <R> SelectTransformingOperation<R, E> mapTo(Class<R> entityClass) {
|
||||||
|
|
||||||
return new SelectTransformingOperation<R, E>(this, (r) -> {
|
Objects.requireNonNull(entityClass, "entityClass is null");
|
||||||
|
|
||||||
Map<String, Object> map = new ValueProviderMap(r, sessionOps.getValueProvider(), entity);
|
HelenusEntity entity = Helenus.entity(entityClass);
|
||||||
return (R) Helenus.map(entityClass, map);
|
|
||||||
|
|
||||||
});
|
this.rowMapper = null;
|
||||||
}
|
|
||||||
|
|
||||||
public <R> SelectTransformingOperation<R, E> map(Function<E, R> fn) {
|
return new SelectTransformingOperation<R, E>(
|
||||||
return new SelectTransformingOperation<R, E>(this, fn);
|
this,
|
||||||
}
|
(r) -> {
|
||||||
|
Map<String, Object> map = new ValueProviderMap(r, sessionOps.getValueProvider(), entity);
|
||||||
|
return (R) Helenus.map(entityClass, map);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public SelectOperation<E> column(Getter<?> getter) {
|
public <R> SelectTransformingOperation<R, E> map(Function<E, R> fn) {
|
||||||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
|
return new SelectTransformingOperation<R, E>(this, fn);
|
||||||
this.props.add(p);
|
}
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectOperation<E> orderBy(Getter<?> getter, OrderingDirection direction) {
|
public SelectOperation<E> column(Getter<?> getter) {
|
||||||
getOrCreateOrdering().add(new Ordered(getter, direction).getOrdering());
|
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
|
||||||
return this;
|
this.props.add(p);
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SelectOperation<E> orderBy(Ordered ordered) {
|
public SelectOperation<E> orderBy(Getter<?> getter, OrderingDirection direction) {
|
||||||
getOrCreateOrdering().add(ordered.getOrdering());
|
getOrCreateOrdering().add(new Ordered(getter, direction).getOrdering());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SelectOperation<E> limit(Integer limit) {
|
public SelectOperation<E> orderBy(Ordered ordered) {
|
||||||
this.limit = limit;
|
getOrCreateOrdering().add(ordered.getOrdering());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SelectOperation<E> allowFiltering() {
|
public SelectOperation<E> limit(Integer limit) {
|
||||||
this.allowFiltering = true;
|
this.limit = limit;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public SelectOperation<E> allowFiltering() {
|
||||||
public BuiltStatement buildStatement() {
|
this.allowFiltering = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
HelenusEntity entity = null;
|
@Override
|
||||||
Selection selection = QueryBuilder.select();
|
public boolean isSessionCacheable() {
|
||||||
|
return isCacheable;
|
||||||
|
}
|
||||||
|
|
||||||
for (HelenusPropertyNode prop : props) {
|
@Override
|
||||||
selection = selection.column(prop.getColumnName());
|
public List<Facet> getFacets() {
|
||||||
|
HelenusEntity entity = props.get(0).getEntity();
|
||||||
|
return entity.getFacets();
|
||||||
|
}
|
||||||
|
|
||||||
if (prop.getProperty().caseSensitiveIndex()) {
|
@Override
|
||||||
allowFiltering = true;
|
public List<Facet> bindFacetValues() {
|
||||||
}
|
HelenusEntity entity = props.get(0).getEntity();
|
||||||
|
List<Facet> boundFacets = new ArrayList<>();
|
||||||
|
|
||||||
if (entity == null) {
|
for (Facet facet : entity.getFacets()) {
|
||||||
entity = prop.getEntity();
|
if (facet instanceof UnboundFacet) {
|
||||||
} else if (entity != prop.getEntity()) {
|
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||||
throw new HelenusMappingException("you can select columns only from a single entity "
|
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||||
+ entity.getMappingInterface() + " or " + prop.getEntity().getMappingInterface());
|
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||||
}
|
if (filters != null) {
|
||||||
}
|
Filter filter = filters.get(prop);
|
||||||
|
if (filter != null) {
|
||||||
if (entity == null) {
|
Object[] postulates = filter.postulateValues();
|
||||||
throw new HelenusMappingException("no entity or table to select data");
|
for (Object p : postulates) {
|
||||||
}
|
binder.setValueForProperty(prop, p.toString());
|
||||||
|
}
|
||||||
Select select = selection.from(entity.getName().toCql());
|
}
|
||||||
|
}
|
||||||
if (ordering != null && !ordering.isEmpty()) {
|
|
||||||
select.orderBy(ordering.toArray(new Ordering[ordering.size()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (limit != null) {
|
|
||||||
select.limit(limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters != null && !filters.isEmpty()) {
|
|
||||||
|
|
||||||
Where where = select.where();
|
|
||||||
|
|
||||||
for (Filter<?> filter : filters) {
|
|
||||||
where.and(filter.getClause(sessionOps.getValuePreparer()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifFilters != null && !ifFilters.isEmpty()) {
|
|
||||||
logger.error("onlyIf conditions " + ifFilters + " would be ignored in the statement " + select);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowFiltering) {
|
|
||||||
select.allowFiltering();
|
|
||||||
}
|
|
||||||
|
|
||||||
return select;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<E> sync() {
|
|
||||||
if (true) {
|
|
||||||
} else {
|
|
||||||
return super.sync();
|
|
||||||
}
|
}
|
||||||
}
|
if (binder.isBound()) {
|
||||||
|
boundFacets.add(binder.bind());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boundFacets.add(facet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boundFacets;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@Override
|
||||||
@Override
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
public Stream<E> transform(ResultSet resultSet) {
|
|
||||||
|
|
||||||
if (rowMapper != null) {
|
HelenusEntity entity = null;
|
||||||
|
Selection selection = QueryBuilder.select();
|
||||||
|
|
||||||
return StreamSupport
|
for (HelenusPropertyNode prop : props) {
|
||||||
.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false)
|
String columnName = prop.getColumnName();
|
||||||
.map(rowMapper);
|
selection = selection.column(columnName);
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
if (entity == null) {
|
||||||
|
entity = prop.getEntity();
|
||||||
|
} else if (entity != prop.getEntity()) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"you can select columns only from a single entity "
|
||||||
|
+ entity.getMappingInterface()
|
||||||
|
+ " or "
|
||||||
|
+ prop.getEntity().getMappingInterface());
|
||||||
|
}
|
||||||
|
|
||||||
return (Stream<E>) StreamSupport
|
if (cached && implementsEntityType) {
|
||||||
.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false);
|
switch (prop.getProperty().getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (entity.equals(prop.getEntity())) {
|
||||||
|
if (!prop.getProperty().getDataType().isCollectionType()) {
|
||||||
|
columnName = prop.getProperty().getColumnName().toCql(false);
|
||||||
|
selection.ttl(columnName).as('"' + CacheUtil.ttlKey(columnName) + '"');
|
||||||
|
selection.writeTime(columnName).as('"' + CacheUtil.writeTimeKey(columnName) + '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
if (entity == null) {
|
||||||
}
|
throw new HelenusMappingException("no entity or table to select data");
|
||||||
|
}
|
||||||
|
|
||||||
private List<Ordering> getOrCreateOrdering() {
|
String tableName = alternateTableName == null ? entity.getName().toCql() : alternateTableName;
|
||||||
if (ordering == null) {
|
Select select = selection.from(tableName);
|
||||||
ordering = new ArrayList<Ordering>();
|
|
||||||
}
|
|
||||||
return ordering;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (ordering != null && !ordering.isEmpty()) {
|
||||||
|
select.orderBy(ordering.toArray(new Ordering[ordering.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit != null) {
|
||||||
|
select.limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters != null && !filters.isEmpty()) {
|
||||||
|
|
||||||
|
Where where = select.where();
|
||||||
|
|
||||||
|
boolean isFirstIndex = true;
|
||||||
|
for (Filter<?> filter : filters.values()) {
|
||||||
|
where.and(filter.getClause(sessionOps.getValuePreparer()));
|
||||||
|
HelenusProperty filterProp = filter.getNode().getProperty();
|
||||||
|
HelenusProperty prop =
|
||||||
|
props
|
||||||
|
.stream()
|
||||||
|
.map(HelenusPropertyNode::getProperty)
|
||||||
|
.filter(thisProp -> thisProp.getPropertyName().equals(filterProp.getPropertyName()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
if (allowFiltering == false && prop != null) {
|
||||||
|
switch (prop.getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
break;
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
default:
|
||||||
|
// When using non-Cassandra-standard 2i types or when using more than one
|
||||||
|
// indexed column or non-indexed columns the query must include ALLOW FILTERING.
|
||||||
|
if (prop.caseSensitiveIndex() == false) {
|
||||||
|
allowFiltering = true;
|
||||||
|
} else if (prop.getIndexName() != null) {
|
||||||
|
allowFiltering |= !isFirstIndex;
|
||||||
|
isFirstIndex = false;
|
||||||
|
} else {
|
||||||
|
allowFiltering = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifFilters != null && !ifFilters.isEmpty()) {
|
||||||
|
LOG.error("onlyIf conditions " + ifFilters + " would be ignored in the statement " + select);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowFiltering) {
|
||||||
|
select.allowFiltering();
|
||||||
|
}
|
||||||
|
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Stream<E> transform(ResultSet resultSet) {
|
||||||
|
if (rowMapper != null) {
|
||||||
|
return StreamSupport.stream(
|
||||||
|
Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false)
|
||||||
|
.map(rowMapper);
|
||||||
|
} else {
|
||||||
|
return (Stream<E>)
|
||||||
|
StreamSupport.stream(
|
||||||
|
Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Ordering> getOrCreateOrdering() {
|
||||||
|
if (ordering == null) {
|
||||||
|
ordering = new ArrayList<Ordering>();
|
||||||
|
}
|
||||||
|
return ordering;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,70 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
*
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* You may obtain a copy of the License at
|
* 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
|
*
|
||||||
*
|
* 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,
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* See the License for the specific language governing permissions and
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* limitations under the License.
|
* See the License for the specific language governing permissions and
|
||||||
*/
|
* limitations under the License.
|
||||||
package net.helenus.core.operation;
|
*/
|
||||||
|
package net.helenus.core.operation;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Stream;
|
import com.datastax.driver.core.ResultSet;
|
||||||
|
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
||||||
import com.datastax.driver.core.ResultSet;
|
import java.util.List;
|
||||||
import com.datastax.driver.core.querybuilder.BuiltStatement;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Stream;
|
||||||
public final class SelectTransformingOperation<R, E>
|
import net.helenus.core.cache.Facet;
|
||||||
extends
|
|
||||||
AbstractFilterStreamOperation<R, SelectTransformingOperation<R, E>> {
|
public final class SelectTransformingOperation<R, E>
|
||||||
|
extends AbstractFilterStreamOperation<R, SelectTransformingOperation<R, E>> {
|
||||||
private final SelectOperation<E> src;
|
|
||||||
private final Function<E, R> fn;
|
private final SelectOperation<E> delegate;
|
||||||
|
private final Function<E, R> fn;
|
||||||
public SelectTransformingOperation(SelectOperation<E> src, Function<E, R> fn) {
|
|
||||||
super(src.sessionOps);
|
public SelectTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
|
||||||
|
super(delegate.sessionOps);
|
||||||
this.src = src;
|
|
||||||
this.fn = fn;
|
this.delegate = delegate;
|
||||||
this.filters = src.filters;
|
this.fn = fn;
|
||||||
this.ifFilters = src.ifFilters;
|
this.filters = delegate.filters;
|
||||||
}
|
this.ifFilters = delegate.ifFilters;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public BuiltStatement buildStatement() {
|
@Override
|
||||||
return src.buildStatement();
|
public List<Facet> bindFacetValues() {
|
||||||
}
|
return delegate.bindFacetValues();
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public Stream<R> transform(ResultSet resultSet) {
|
@Override
|
||||||
return src.transform(resultSet).map(fn);
|
public List<Facet> getFacets() {
|
||||||
}
|
return delegate.getFacets();
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public BuiltStatement buildStatement(boolean cached) {
|
||||||
|
return delegate.buildStatement(cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<R> transform(ResultSet resultSet) {
|
||||||
|
return delegate.transform(resultSet).map(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSessionCacheable() {
|
||||||
|
return delegate.isSessionCacheable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean ignoreCache() {
|
||||||
|
return delegate.ignoreCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,36 +20,41 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public enum DefaultPrimitiveTypes {
|
public enum DefaultPrimitiveTypes {
|
||||||
|
BOOLEAN(boolean.class, false),
|
||||||
|
BYTE(byte.class, (byte) 0x0),
|
||||||
|
CHAR(char.class, (char) 0x0),
|
||||||
|
SHORT(short.class, (short) 0),
|
||||||
|
INT(int.class, 0),
|
||||||
|
LONG(long.class, 0L),
|
||||||
|
FLOAT(float.class, 0.0f),
|
||||||
|
DOUBLE(double.class, 0.0);
|
||||||
|
|
||||||
BOOLEAN(boolean.class, false), BYTE(byte.class, (byte) 0x0), CHAR(char.class, (char) 0x0), SHORT(short.class,
|
private static final Map<Class<?>, DefaultPrimitiveTypes> map =
|
||||||
(short) 0), INT(int.class, 0), LONG(long.class, 0L), FLOAT(float.class, 0.0f), DOUBLE(double.class, 0.0);
|
new HashMap<Class<?>, DefaultPrimitiveTypes>();
|
||||||
|
|
||||||
private final Class<?> primitiveClass;
|
static {
|
||||||
private final Object defaultValue;
|
for (DefaultPrimitiveTypes type : DefaultPrimitiveTypes.values()) {
|
||||||
|
map.put(type.getPrimitiveClass(), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final static Map<Class<?>, DefaultPrimitiveTypes> map = new HashMap<Class<?>, DefaultPrimitiveTypes>();
|
private final Class<?> primitiveClass;
|
||||||
|
private final Object defaultValue;
|
||||||
|
|
||||||
static {
|
private DefaultPrimitiveTypes(Class<?> primitiveClass, Object defaultValue) {
|
||||||
for (DefaultPrimitiveTypes type : DefaultPrimitiveTypes.values()) {
|
this.primitiveClass = primitiveClass;
|
||||||
map.put(type.getPrimitiveClass(), type);
|
this.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private DefaultPrimitiveTypes(Class<?> primitiveClass, Object defaultValue) {
|
public static DefaultPrimitiveTypes lookup(Class<?> primitiveClass) {
|
||||||
this.primitiveClass = primitiveClass;
|
return map.get(primitiveClass);
|
||||||
this.defaultValue = defaultValue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static DefaultPrimitiveTypes lookup(Class<?> primitiveClass) {
|
public Class<?> getPrimitiveClass() {
|
||||||
return map.get(primitiveClass);
|
return primitiveClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getPrimitiveClass() {
|
|
||||||
return primitiveClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getDefaultValue() {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public Object getDefaultValue() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/main/java/net/helenus/core/reflect/Drafted.java
Normal file
28
src/main/java/net/helenus/core/reflect/Drafted.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface Drafted<T> extends MapExportable {
|
||||||
|
|
||||||
|
Set<String> mutated();
|
||||||
|
|
||||||
|
T build();
|
||||||
|
|
||||||
|
Set<String> read();
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,15 +16,18 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import com.datastax.driver.core.Metadata;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
|
|
||||||
public interface DslExportable {
|
public interface DslExportable {
|
||||||
|
|
||||||
public static final String GET_ENTITY_METHOD = "getHelenusMappingEntity";
|
String GET_ENTITY_METHOD = "getHelenusMappingEntity";
|
||||||
public static final String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode";
|
String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode";
|
||||||
|
String SET_METADATA_METHOD = "setCassandraMetadataForHelenusSession";
|
||||||
|
|
||||||
HelenusEntity getHelenusMappingEntity();
|
HelenusEntity getHelenusMappingEntity();
|
||||||
|
|
||||||
HelenusPropertyNode getParentDslHelenusPropertyNode();
|
HelenusPropertyNode getParentDslHelenusPropertyNode();
|
||||||
|
|
||||||
|
void setCassandraMetadataForHelenusSession(Metadata metadata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,18 +16,14 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import com.datastax.driver.core.*;
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.datastax.driver.core.*;
|
|
||||||
|
|
||||||
import net.helenus.core.Helenus;
|
import net.helenus.core.Helenus;
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
import net.helenus.mapping.HelenusMappingEntity;
|
import net.helenus.mapping.HelenusMappingEntity;
|
||||||
|
@ -39,144 +36,178 @@ import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
public class DslInvocationHandler<E> implements InvocationHandler {
|
public class DslInvocationHandler<E> implements InvocationHandler {
|
||||||
|
|
||||||
private final HelenusEntity entity;
|
private final Class<E> iface;
|
||||||
private final Optional<HelenusPropertyNode> parent;
|
private final ClassLoader classLoader;
|
||||||
|
private final Optional<HelenusPropertyNode> parent;
|
||||||
|
private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>();
|
||||||
|
private final Map<Method, Object> udtMap = new HashMap<Method, Object>();
|
||||||
|
private final Map<Method, Object> tupleMap = new HashMap<Method, Object>();
|
||||||
|
private HelenusEntity entity = null;
|
||||||
|
private Metadata metadata = null;
|
||||||
|
|
||||||
private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>();
|
public DslInvocationHandler(
|
||||||
|
Class<E> iface,
|
||||||
|
ClassLoader classLoader,
|
||||||
|
Optional<HelenusPropertyNode> parent,
|
||||||
|
Metadata metadata) {
|
||||||
|
|
||||||
private final Map<Method, Object> udtMap = new HashMap<Method, Object>();
|
this.metadata = metadata;
|
||||||
private final Map<Method, Object> tupleMap = new HashMap<Method, Object>();
|
this.parent = parent;
|
||||||
|
this.iface = iface;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
public DslInvocationHandler(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, Metadata metadata) {
|
public void setCassandraMetadataForHelenusSession(Metadata metadata) {
|
||||||
|
if (metadata != null) {
|
||||||
|
this.metadata = metadata;
|
||||||
|
entity = init(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.entity = new HelenusMappingEntity(iface, metadata);
|
private HelenusEntity init(Metadata metadata) {
|
||||||
this.parent = parent;
|
HelenusEntity entity = new HelenusMappingEntity(iface, metadata);
|
||||||
|
Collection<HelenusProperty> properties = entity.getOrderedProperties();
|
||||||
|
if (properties != null) {
|
||||||
|
for (HelenusProperty prop : properties) {
|
||||||
|
|
||||||
if (this.entity != null) {
|
map.put(prop.getGetterMethod(), prop);
|
||||||
for (HelenusProperty prop : entity.getOrderedProperties()) {
|
|
||||||
|
|
||||||
map.put(prop.getGetterMethod(), prop);
|
AbstractDataType type = prop.getDataType();
|
||||||
|
Class<?> javaType = prop.getJavaType();
|
||||||
|
|
||||||
AbstractDataType type = prop.getDataType();
|
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) {
|
||||||
Class<?> javaType = prop.getJavaType();
|
|
||||||
|
|
||||||
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) {
|
Object childDsl =
|
||||||
|
Helenus.dsl(
|
||||||
|
javaType,
|
||||||
|
classLoader,
|
||||||
|
Optional.of(new HelenusPropertyNode(prop, parent)),
|
||||||
|
metadata);
|
||||||
|
|
||||||
Object childDsl = Helenus.dsl(javaType, classLoader,
|
udtMap.put(prop.getGetterMethod(), childDsl);
|
||||||
Optional.of(new HelenusPropertyNode(prop, parent)), metadata);
|
}
|
||||||
|
|
||||||
udtMap.put(prop.getGetterMethod(), childDsl);
|
if (type instanceof DTDataType) {
|
||||||
}
|
DTDataType dataType = (DTDataType) type;
|
||||||
|
|
||||||
if (type instanceof DTDataType) {
|
if (dataType.getDataType() instanceof TupleType
|
||||||
DTDataType dataType = (DTDataType) type;
|
&& !TupleValue.class.isAssignableFrom(javaType)) {
|
||||||
|
|
||||||
if (dataType.getDataType() instanceof TupleType && !TupleValue.class.isAssignableFrom(javaType)) {
|
Object childDsl =
|
||||||
|
Helenus.dsl(
|
||||||
|
javaType,
|
||||||
|
classLoader,
|
||||||
|
Optional.of(new HelenusPropertyNode(prop, parent)),
|
||||||
|
metadata);
|
||||||
|
|
||||||
Object childDsl = Helenus.dsl(javaType, classLoader,
|
tupleMap.put(prop.getGetterMethod(), childDsl);
|
||||||
Optional.of(new HelenusPropertyNode(prop, parent)), metadata);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tupleMap.put(prop.getGetterMethod(), childDsl);
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
}
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
|
||||||
|
HelenusEntity entity = this.entity;
|
||||||
|
String methodName = method.getName();
|
||||||
|
|
||||||
|
if ("equals".equals(methodName) && method.getParameterCount() == 1) {
|
||||||
|
Object otherObj = args[0];
|
||||||
|
if (otherObj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Proxy.isProxyClass(otherObj.getClass())) {
|
||||||
|
return this == Proxy.getInvocationHandler(otherObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DslExportable.SET_METADATA_METHOD.equals(methodName)
|
||||||
|
&& args.length == 1
|
||||||
|
&& args[0] instanceof Metadata) {
|
||||||
|
if (metadata == null) {
|
||||||
|
this.setCassandraMetadataForHelenusSession((Metadata) args[0]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
|
||||||
|
throw new HelenusException("invalid getter method " + method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("hashCode".equals(methodName)) {
|
||||||
|
return hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
|
||||||
|
return parent.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
entity = init(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("toString".equals(methodName)) {
|
||||||
|
return entity.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DslExportable.GET_ENTITY_METHOD.equals(methodName)) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
HelenusProperty prop = map.get(method);
|
||||||
|
if (prop == null) {
|
||||||
|
prop = entity.getProperty(methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop != null) {
|
||||||
|
|
||||||
|
AbstractDataType type = prop.getDataType();
|
||||||
|
|
||||||
|
if (type instanceof UDTDataType) {
|
||||||
|
|
||||||
|
Object childDsl = udtMap.get(method);
|
||||||
|
|
||||||
|
if (childDsl != null) {
|
||||||
|
return childDsl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type instanceof DTDataType) {
|
||||||
|
DTDataType dataType = (DTDataType) type;
|
||||||
|
DataType dt = dataType.getDataType();
|
||||||
|
|
||||||
|
switch (dt.getName()) {
|
||||||
|
case TUPLE:
|
||||||
|
Object childDsl = tupleMap.get(method);
|
||||||
|
|
||||||
|
if (childDsl != null) {
|
||||||
|
return childDsl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SET:
|
||||||
|
return new SetDsl(new HelenusPropertyNode(prop, parent));
|
||||||
|
|
||||||
|
case LIST:
|
||||||
|
return new ListDsl(new HelenusPropertyNode(prop, parent));
|
||||||
|
|
||||||
|
case MAP:
|
||||||
|
return new MapDsl(new HelenusPropertyNode(prop, parent));
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
throw new DslPropertyException(new HelenusPropertyNode(prop, parent));
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
}
|
||||||
|
|
||||||
String methodName = method.getName();
|
|
||||||
|
|
||||||
if ("equals".equals(methodName) && method.getParameterCount() == 1) {
|
|
||||||
Object otherObj = args[0];
|
|
||||||
if (otherObj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Proxy.isProxyClass(otherObj.getClass())) {
|
|
||||||
return this == Proxy.getInvocationHandler(otherObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
|
|
||||||
throw new HelenusException("invalid getter method " + method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("hashCode".equals(methodName)) {
|
|
||||||
return hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("toString".equals(methodName)) {
|
|
||||||
return entity.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DslExportable.GET_ENTITY_METHOD.equals(methodName)) {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
|
|
||||||
return parent.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
HelenusProperty prop = map.get(method);
|
|
||||||
if (prop == null) {
|
|
||||||
prop = entity.getProperty(methodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop != null) {
|
|
||||||
|
|
||||||
AbstractDataType type = prop.getDataType();
|
|
||||||
|
|
||||||
if (type instanceof UDTDataType) {
|
|
||||||
|
|
||||||
Object childDsl = udtMap.get(method);
|
|
||||||
|
|
||||||
if (childDsl != null) {
|
|
||||||
return childDsl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type instanceof DTDataType) {
|
|
||||||
DTDataType dataType = (DTDataType) type;
|
|
||||||
DataType dt = dataType.getDataType();
|
|
||||||
|
|
||||||
switch (dt.getName()) {
|
|
||||||
|
|
||||||
case TUPLE :
|
|
||||||
|
|
||||||
Object childDsl = tupleMap.get(method);
|
|
||||||
|
|
||||||
if (childDsl != null) {
|
|
||||||
return childDsl;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SET :
|
|
||||||
return new SetDsl(new HelenusPropertyNode(prop, parent));
|
|
||||||
|
|
||||||
case LIST :
|
|
||||||
return new ListDsl(new HelenusPropertyNode(prop, parent));
|
|
||||||
|
|
||||||
case MAP :
|
|
||||||
return new MapDsl(new HelenusPropertyNode(prop, parent));
|
|
||||||
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new DslPropertyException(new HelenusPropertyNode(prop, parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HelenusException("invalid method call " + method);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
throw new HelenusException("invalid method call " + method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
73
src/main/java/net/helenus/core/reflect/Entity.java
Normal file
73
src/main/java/net/helenus/core/reflect/Entity.java
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import net.helenus.core.Getter;
|
||||||
|
|
||||||
|
public interface Entity {
|
||||||
|
String WRITTEN_AT_METHOD = "writtenAt";
|
||||||
|
String TTL_OF_METHOD = "ttlOf";
|
||||||
|
String TOKEN_OF_METHOD = "tokenOf";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The write time for the property in question referenced by the getter.
|
||||||
|
*
|
||||||
|
* @param getter the property getter
|
||||||
|
* @return the timestamp associated with the property identified by the getter
|
||||||
|
*/
|
||||||
|
default Long writtenAt(Getter getter) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The write time for the property in question referenced by the property name.
|
||||||
|
*
|
||||||
|
* @param prop the name of a property in this entity
|
||||||
|
* @return the timestamp associated with the property identified by the property name if it exists
|
||||||
|
*/
|
||||||
|
default Long writtenAt(String prop) {
|
||||||
|
return 0L;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time-to-live for the property in question referenced by the getter.
|
||||||
|
*
|
||||||
|
* @param getter the property getter
|
||||||
|
* @return the time-to-live in seconds associated with the property identified by the getter
|
||||||
|
*/
|
||||||
|
default Integer ttlOf(Getter getter) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time-to-live for the property in question referenced by the property name.
|
||||||
|
*
|
||||||
|
* @param prop the name of a property in this entity
|
||||||
|
* @return the time-to-live in seconds associated with the property identified by the property name if it exists
|
||||||
|
*/
|
||||||
|
default Integer ttlOf(String prop) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The token (partition identifier) for this entity which can change over time if
|
||||||
|
* the cluster grows or shrinks but should be stable otherwise.
|
||||||
|
*
|
||||||
|
* @return the token for the entity
|
||||||
|
*/
|
||||||
|
default Long tokenOf() { return 0L; }
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,9 +20,7 @@ import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import javax.validation.ConstraintValidator;
|
import javax.validation.ConstraintValidator;
|
||||||
|
|
||||||
import net.helenus.core.SessionRepository;
|
import net.helenus.core.SessionRepository;
|
||||||
import net.helenus.mapping.*;
|
import net.helenus.mapping.*;
|
||||||
import net.helenus.mapping.type.AbstractDataType;
|
import net.helenus.mapping.type.AbstractDataType;
|
||||||
|
@ -29,77 +28,84 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class HelenusNamedProperty implements HelenusProperty {
|
public final class HelenusNamedProperty implements HelenusProperty {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
public HelenusNamedProperty(String name) {
|
public HelenusNamedProperty(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HelenusEntity getEntity() {
|
public HelenusEntity getEntity() {
|
||||||
throw new HelenusMappingException("will never called");
|
throw new HelenusMappingException("will never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPropertyName() {
|
public String getPropertyName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Method getGetterMethod() {
|
public Method getGetterMethod() {
|
||||||
throw new HelenusMappingException("will never called");
|
throw new HelenusMappingException("will never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityName getColumnName() {
|
public IdentityName getColumnName() {
|
||||||
return IdentityName.of(name, false);
|
return IdentityName.of(name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<IdentityName> getIndexName() {
|
public Optional<IdentityName> getIndexName() {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean caseSensitiveIndex() { return false; }
|
public boolean caseSensitiveIndex() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getJavaType() {
|
public boolean isIdempotent() {
|
||||||
throw new HelenusMappingException("will never called");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractDataType getDataType() {
|
public Class<?> getJavaType() {
|
||||||
throw new HelenusMappingException("will never called");
|
throw new HelenusMappingException("will never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ColumnType getColumnType() {
|
public AbstractDataType getDataType() {
|
||||||
return ColumnType.COLUMN;
|
throw new HelenusMappingException("will never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOrdinal() {
|
public ColumnType getColumnType() {
|
||||||
return 0;
|
return ColumnType.COLUMN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrderingDirection getOrdering() {
|
public int getOrdinal() {
|
||||||
return OrderingDirection.ASC;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Function<Object, Object>> getReadConverter(SessionRepository repository) {
|
public OrderingDirection getOrdering() {
|
||||||
return Optional.empty();
|
return OrderingDirection.ASC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Function<Object, Object>> getWriteConverter(SessionRepository repository) {
|
public Optional<Function<Object, Object>> getReadConverter(SessionRepository repository) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConstraintValidator<? extends Annotation, ?>[] getValidators() {
|
public Optional<Function<Object, Object>> getWriteConverter(SessionRepository repository) {
|
||||||
return MappingUtil.EMPTY_VALIDATORS;
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConstraintValidator<? extends Annotation, ?>[] getValidators() {
|
||||||
|
return MappingUtil.EMPTY_VALIDATORS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,92 +18,89 @@ package net.helenus.core.reflect;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import net.helenus.mapping.HelenusEntity;
|
import net.helenus.mapping.HelenusEntity;
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
|
|
||||||
public final class HelenusPropertyNode implements Iterable<HelenusProperty> {
|
public final class HelenusPropertyNode implements Iterable<HelenusProperty> {
|
||||||
|
|
||||||
private final HelenusProperty prop;
|
private final HelenusProperty prop;
|
||||||
private final Optional<HelenusPropertyNode> next;
|
private final Optional<HelenusPropertyNode> next;
|
||||||
|
|
||||||
public HelenusPropertyNode(HelenusProperty prop, Optional<HelenusPropertyNode> next) {
|
public HelenusPropertyNode(HelenusProperty prop, Optional<HelenusPropertyNode> next) {
|
||||||
this.prop = prop;
|
this.prop = prop;
|
||||||
this.next = next;
|
this.next = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getColumnName() {
|
public String getColumnName() {
|
||||||
if (next.isPresent()) {
|
if (next.isPresent()) {
|
||||||
|
|
||||||
List<String> columnNames = new ArrayList<String>();
|
List<String> columnNames = new ArrayList<String>();
|
||||||
for (HelenusProperty p : this) {
|
for (HelenusProperty p : this) {
|
||||||
columnNames.add(p.getColumnName().toCql(true));
|
columnNames.add(p.getColumnName().toCql(true));
|
||||||
}
|
}
|
||||||
Collections.reverse(columnNames);
|
Collections.reverse(columnNames);
|
||||||
|
|
||||||
if (prop instanceof HelenusNamedProperty) {
|
if (prop instanceof HelenusNamedProperty) {
|
||||||
int size = columnNames.size();
|
int size = columnNames.size();
|
||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
for (int i = 0; i != size - 1; ++i) {
|
for (int i = 0; i != size - 1; ++i) {
|
||||||
if (str.length() != 0) {
|
if (str.length() != 0) {
|
||||||
str.append(".");
|
str.append(".");
|
||||||
}
|
}
|
||||||
str.append(columnNames.get(i));
|
str.append(columnNames.get(i));
|
||||||
}
|
}
|
||||||
str.append("[").append(columnNames.get(size - 1)).append("]");
|
str.append("[").append(columnNames.get(size - 1)).append("]");
|
||||||
return str.toString();
|
return str.toString();
|
||||||
} else {
|
} else {
|
||||||
return columnNames.stream().collect(Collectors.joining("."));
|
return columnNames.stream().collect(Collectors.joining("."));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return prop.getColumnName().toCql();
|
return prop.getColumnName().toCql();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusEntity getEntity() {
|
public HelenusEntity getEntity() {
|
||||||
if (next.isPresent()) {
|
if (next.isPresent()) {
|
||||||
HelenusProperty last = prop;
|
HelenusProperty last = prop;
|
||||||
for (HelenusProperty p : this) {
|
for (HelenusProperty p : this) {
|
||||||
last = p;
|
last = p;
|
||||||
}
|
}
|
||||||
return last.getEntity();
|
return last.getEntity();
|
||||||
} else {
|
} else {
|
||||||
return prop.getEntity();
|
return prop.getEntity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusProperty getProperty() {
|
public HelenusProperty getProperty() {
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<HelenusPropertyNode> getNext() {
|
public Optional<HelenusPropertyNode> getNext() {
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<HelenusProperty> iterator() {
|
public Iterator<HelenusProperty> iterator() {
|
||||||
return new PropertyNodeIterator(Optional.of(this));
|
return new PropertyNodeIterator(Optional.of(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PropertyNodeIterator implements Iterator<HelenusProperty> {
|
private static class PropertyNodeIterator implements Iterator<HelenusProperty> {
|
||||||
|
|
||||||
private Optional<HelenusPropertyNode> next;
|
private Optional<HelenusPropertyNode> next;
|
||||||
|
|
||||||
public PropertyNodeIterator(Optional<HelenusPropertyNode> next) {
|
public PropertyNodeIterator(Optional<HelenusPropertyNode> next) {
|
||||||
this.next = next;
|
this.next = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return next.isPresent();
|
return next.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HelenusProperty next() {
|
|
||||||
HelenusPropertyNode node = next.get();
|
|
||||||
next = node.next;
|
|
||||||
return node.prop;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HelenusProperty next() {
|
||||||
|
HelenusPropertyNode node = next.get();
|
||||||
|
next = node.next;
|
||||||
|
return node.prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,165 +17,164 @@
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
import net.helenus.support.DslPropertyException;
|
import net.helenus.support.DslPropertyException;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class ListDsl<V> implements List<V> {
|
public final class ListDsl<V> implements List<V> {
|
||||||
|
|
||||||
private final HelenusPropertyNode parent;
|
private final HelenusPropertyNode parent;
|
||||||
|
|
||||||
public ListDsl(HelenusPropertyNode parent) {
|
public ListDsl(HelenusPropertyNode parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusPropertyNode getParent() {
|
public HelenusPropertyNode getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get(int index) {
|
public V get(int index) {
|
||||||
HelenusProperty prop = new HelenusNamedProperty(Integer.toString(index));
|
HelenusProperty prop = new HelenusNamedProperty(Integer.toString(index));
|
||||||
throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent)));
|
throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object o) {
|
public boolean contains(Object o) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<V> iterator() {
|
public Iterator<V> iterator() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] toArray() {
|
public Object[] toArray() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T[] toArray(T[] a) {
|
public <T> T[] toArray(T[] a) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(V e) {
|
public boolean add(V e) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(Object o) {
|
public boolean remove(Object o) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsAll(Collection<?> c) {
|
public boolean containsAll(Collection<?> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(Collection<? extends V> c) {
|
public boolean addAll(Collection<? extends V> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(int index, Collection<? extends V> c) {
|
public boolean addAll(int index, Collection<? extends V> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeAll(Collection<?> c) {
|
public boolean removeAll(Collection<?> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean retainAll(Collection<?> c) {
|
public boolean retainAll(Collection<?> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V set(int index, V element) {
|
public V set(int index, V element) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(int index, V element) {
|
public void add(int index, V element) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V remove(int index) {
|
public V remove(int index) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int indexOf(Object o) {
|
public int indexOf(Object o) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int lastIndexOf(Object o) {
|
public int lastIndexOf(Object o) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListIterator<V> listIterator() {
|
public ListIterator<V> listIterator() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListIterator<V> listIterator(int index) {
|
public ListIterator<V> listIterator(int index) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<V> subList(int fromIndex, int toIndex) {
|
public List<V> subList(int fromIndex, int toIndex) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwShouldNeverCall() {
|
private void throwShouldNeverCall() {
|
||||||
throw new HelenusMappingException("should be never called");
|
throw new HelenusMappingException("should be never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ListDsl";
|
return "ListDsl";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,100 +20,98 @@ import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.helenus.mapping.HelenusProperty;
|
import net.helenus.mapping.HelenusProperty;
|
||||||
import net.helenus.support.DslPropertyException;
|
import net.helenus.support.DslPropertyException;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class MapDsl<K, V> implements Map<K, V> {
|
public final class MapDsl<K, V> implements Map<K, V> {
|
||||||
|
|
||||||
private final HelenusPropertyNode parent;
|
private final HelenusPropertyNode parent;
|
||||||
|
|
||||||
public MapDsl(HelenusPropertyNode parent) {
|
public MapDsl(HelenusPropertyNode parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusPropertyNode getParent() {
|
public HelenusPropertyNode getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get(Object key) {
|
public V get(Object key) {
|
||||||
HelenusProperty prop = new HelenusNamedProperty(key.toString());
|
HelenusProperty prop = new HelenusNamedProperty(key.toString());
|
||||||
throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent)));
|
throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
public boolean containsKey(Object key) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsValue(Object value) {
|
public boolean containsValue(Object value) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V put(K key, V value) {
|
public V put(K key, V value) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V remove(Object key) {
|
public V remove(Object key) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putAll(Map<? extends K, ? extends V> m) {
|
public void putAll(Map<? extends K, ? extends V> m) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<K> keySet() {
|
public Set<K> keySet() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<V> values() {
|
public Collection<V> values() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<java.util.Map.Entry<K, V>> entrySet() {
|
public Set<java.util.Map.Entry<K, V>> entrySet() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwShouldNeverCall() {
|
private void throwShouldNeverCall() {
|
||||||
throw new HelenusMappingException("should be never called");
|
throw new HelenusMappingException("should be never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "MapDsl";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MapDsl";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,11 +17,25 @@
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import net.helenus.core.Getter;
|
||||||
|
|
||||||
public interface MapExportable {
|
public interface MapExportable {
|
||||||
|
String TO_MAP_METHOD = "toMap";
|
||||||
|
String TO_READ_SET_METHOD = "toReadSet";
|
||||||
|
String PUT_METHOD = "put";
|
||||||
|
|
||||||
public static final String TO_MAP_METHOD = "toMap";
|
Map<String, Object> toMap();
|
||||||
|
|
||||||
Map<String, Object> toMap();
|
default Map<String, Object> toMap(boolean mutable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default Set<String> toReadSet() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default void put(String key, Object value) {}
|
||||||
|
|
||||||
|
default <T> void put(Getter<T> getter, T value) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,97 +16,279 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Map;
|
import net.helenus.core.Getter;
|
||||||
|
import net.helenus.core.Helenus;
|
||||||
|
import net.helenus.core.cache.CacheUtil;
|
||||||
|
import net.helenus.mapping.MappingUtil;
|
||||||
|
import net.helenus.mapping.annotation.Transient;
|
||||||
|
import net.helenus.mapping.value.ValueProviderMap;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
public class MapperInvocationHandler<E> implements InvocationHandler {
|
public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
|
||||||
|
private static final long serialVersionUID = -7044209982830584984L;
|
||||||
|
|
||||||
private final Map<String, Object> src;
|
private Map<String, Object> src;
|
||||||
private final Class<E> iface;
|
private final Set<String> read = new HashSet<String>();
|
||||||
|
private final Class<E> iface;
|
||||||
|
|
||||||
public MapperInvocationHandler(Class<E> iface, Map<String, Object> src) {
|
public MapperInvocationHandler(Class<E> iface, Map<String, Object> src) {
|
||||||
this.src = src;
|
this.src = src;
|
||||||
this.iface = iface;
|
this.iface = iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
// NOTE: This is reflection magic to invoke (non-recursively) a default method
|
||||||
|
// implemented on an interface
|
||||||
|
// that we've proxied (in ReflectionDslInstantiator). I found the answer in this
|
||||||
|
// article.
|
||||||
|
// https://zeroturnaround.com/rebellabs/recognize-and-conquer-java-proxies-default-methods-and-method-handles/
|
||||||
|
|
||||||
if (method.isDefault()) {
|
// First, we need an instance of a private inner-class found in MethodHandles.
|
||||||
// NOTE: This is reflection magic to invoke (non-recursively) a default method implemented on an interface
|
Constructor<MethodHandles.Lookup> constructor =
|
||||||
// that we've proxied (in ReflectionDslInstantiator). I found the answer in this article.
|
MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
||||||
// https://zeroturnaround.com/rebellabs/recognize-and-conquer-java-proxies-default-methods-and-method-handles/
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
// First, we need an instance of a private inner-class found in MethodHandles.
|
// Now we need to lookup and invoke special the default method on the interface
|
||||||
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
// class.
|
||||||
constructor.setAccessible(true);
|
final Class<?> declaringClass = method.getDeclaringClass();
|
||||||
|
Object result =
|
||||||
|
constructor
|
||||||
|
.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
|
||||||
|
.unreflectSpecial(method, declaringClass)
|
||||||
|
.bindTo(proxy)
|
||||||
|
.invokeWithArguments(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Now we need to lookup and invoke special the default method on the interface class.
|
private Object writeReplace() {
|
||||||
final Class<?> declaringClass = method.getDeclaringClass();
|
return new SerializationProxy<E>(this);
|
||||||
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
|
}
|
||||||
.unreflectSpecial(method, declaringClass)
|
|
||||||
.bindTo(proxy)
|
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
|
||||||
.invokeWithArguments(args);
|
throw new InvalidObjectException("Proxy required.");
|
||||||
return result;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
|
||||||
|
// Transient, default methods should simply be invoked as-is.
|
||||||
|
if (method.isDefault() && method.getDeclaredAnnotation(Transient.class) != null) {
|
||||||
|
return invokeDefault(proxy, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
String methodName = method.getName();
|
||||||
|
|
||||||
|
if ("equals".equals(methodName) && method.getParameterCount() == 1) {
|
||||||
|
Object otherObj = args[0];
|
||||||
|
if (otherObj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Proxy.isProxyClass(otherObj.getClass())) {
|
||||||
|
if (this == Proxy.getInvocationHandler(otherObj)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (otherObj instanceof MapExportable) {
|
||||||
|
return MappingUtil.compareMaps((MapExportable) otherObj, src);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
String methodName = method.getName();
|
if (MapExportable.PUT_METHOD.equals(methodName) && method.getParameterCount() == 2) {
|
||||||
|
final String key;
|
||||||
|
if (args[0] instanceof String) {
|
||||||
|
key = (String) args[0];
|
||||||
|
} else if (args[0] instanceof Getter) {
|
||||||
|
key = MappingUtil.resolveMappingProperty((Getter) args[0]).getProperty().getPropertyName();
|
||||||
|
} else {
|
||||||
|
key = null;
|
||||||
|
}
|
||||||
|
if (key != null) {
|
||||||
|
final Object value = (Object) args[1];
|
||||||
|
if (src instanceof ValueProviderMap) {
|
||||||
|
this.src = fromValueProviderMap(src);
|
||||||
|
}
|
||||||
|
src.put(key, value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if ("equals".equals(methodName) && method.getParameterCount() == 1) {
|
if (Entity.WRITTEN_AT_METHOD.equals(methodName) && method.getParameterCount() == 1) {
|
||||||
Object otherObj = args[0];
|
final String key;
|
||||||
if (otherObj == null) {
|
if (args[0] instanceof String) {
|
||||||
return false;
|
key = CacheUtil.writeTimeKey((String) args[0]);
|
||||||
}
|
} else if (args[0] instanceof Getter) {
|
||||||
if (Proxy.isProxyClass(otherObj.getClass())) {
|
Getter getter = (Getter) args[0];
|
||||||
return this == Proxy.getInvocationHandler(otherObj);
|
key =
|
||||||
}
|
CacheUtil.writeTimeKey(
|
||||||
return false;
|
MappingUtil.resolveMappingProperty(getter)
|
||||||
}
|
.getProperty()
|
||||||
|
.getColumnName()
|
||||||
|
.toCql(false));
|
||||||
|
} else {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
Long v = (Long) src.get(key);
|
||||||
|
if (v != null) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
|
if (Entity.TOKEN_OF_METHOD.equals(methodName) && method.getParameterCount() == 0) {
|
||||||
throw new HelenusException("invalid getter method " + method);
|
Long v = (Long) src.get("");
|
||||||
}
|
if (v != null) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
if ("hashCode".equals(methodName)) {
|
if (Entity.TTL_OF_METHOD.equals(methodName) && method.getParameterCount() == 1) {
|
||||||
return hashCode();
|
final String key;
|
||||||
}
|
if (args[0] instanceof String) {
|
||||||
|
key = CacheUtil.ttlKey((String) args[0]);
|
||||||
|
} else if (args[0] instanceof Getter) {
|
||||||
|
Getter getter = (Getter) args[0];
|
||||||
|
key =
|
||||||
|
CacheUtil.ttlKey(
|
||||||
|
MappingUtil.resolveMappingProperty(getter)
|
||||||
|
.getProperty()
|
||||||
|
.getColumnName()
|
||||||
|
.toCql(false));
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int v[] = (int[]) src.get(key);
|
||||||
|
if (v != null) {
|
||||||
|
return v[0];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ("toString".equals(methodName)) {
|
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
|
||||||
return iface.getSimpleName() + ": " + src.toString();
|
if (method.getParameterCount() == 1 && args[0] instanceof Boolean) {
|
||||||
}
|
if ((boolean) args[0] == true) {
|
||||||
|
return fromValueProviderMap(src, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(src);
|
||||||
|
}
|
||||||
|
|
||||||
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
|
if (MapExportable.TO_READ_SET_METHOD.equals(methodName)) {
|
||||||
return Collections.unmodifiableMap(src);
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object value = src.get(methodName);
|
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
|
||||||
|
throw new HelenusException("invalid getter method " + method);
|
||||||
|
}
|
||||||
|
|
||||||
if (value == null) {
|
if ("hashCode".equals(methodName)) {
|
||||||
|
return hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
Class<?> returnType = method.getReturnType();
|
if ("toString".equals(methodName)) {
|
||||||
|
return iface.getSimpleName() + ": " + src.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (returnType.isPrimitive()) {
|
if ("writeReplace".equals(methodName)) {
|
||||||
|
return new SerializationProxy(this);
|
||||||
|
}
|
||||||
|
|
||||||
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
if ("readObject".equals(methodName)) {
|
||||||
if (type == null) {
|
throw new InvalidObjectException("Proxy required.");
|
||||||
throw new HelenusException("unknown primitive type " + returnType);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return type.getDefaultValue();
|
if ("dsl".equals(methodName)) {
|
||||||
|
return Helenus.dsl(iface);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
final Object value = src.get(methodName);
|
||||||
|
read.add(methodName);
|
||||||
|
|
||||||
}
|
if (value == null) {
|
||||||
|
|
||||||
return value;
|
Class<?> returnType = method.getReturnType();
|
||||||
}
|
|
||||||
|
|
||||||
|
// Default implementations of non-Transient methods in entities are the default
|
||||||
|
// value when the map contains 'null'.
|
||||||
|
if (method.isDefault()) {
|
||||||
|
return invokeDefault(proxy, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if the return type of the method is a primitive Java type then
|
||||||
|
// we'll return the standard default values to avoid a NPE in user code.
|
||||||
|
if (returnType.isPrimitive()) {
|
||||||
|
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
||||||
|
if (type == null) {
|
||||||
|
throw new HelenusException("unknown primitive type " + returnType);
|
||||||
|
}
|
||||||
|
return type.getDefaultValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, Object> fromValueProviderMap(Map v) {
|
||||||
|
return fromValueProviderMap(v, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, Object> fromValueProviderMap(Map v, boolean mutable) {
|
||||||
|
if (v instanceof ValueProviderMap) {
|
||||||
|
Map<String, Object> m = new HashMap<String, Object>(v.size());
|
||||||
|
Set<String> keys = v.keySet();
|
||||||
|
for (String key : keys) {
|
||||||
|
Object value = v.get(key);
|
||||||
|
if (value != null && mutable) {
|
||||||
|
if (ImmutableList.class.isAssignableFrom(value.getClass())) {
|
||||||
|
m.put(key, new ArrayList((List) value));
|
||||||
|
} else if (ImmutableMap.class.isAssignableFrom(value.getClass())) {
|
||||||
|
m.put(key, new HashMap((Map) value));
|
||||||
|
} else if (ImmutableSet.class.isAssignableFrom(value.getClass())) {
|
||||||
|
m.put(key, new HashSet((Set) value));
|
||||||
|
} else {
|
||||||
|
m.put(key, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SerializationProxy<E> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -5617583940055969353L;
|
||||||
|
|
||||||
|
private final Class<E> iface;
|
||||||
|
private final Map<String, Object> src;
|
||||||
|
|
||||||
|
public SerializationProxy(MapperInvocationHandler mapper) {
|
||||||
|
this.iface = mapper.iface;
|
||||||
|
if (mapper.src instanceof ValueProviderMap) {
|
||||||
|
this.src = fromValueProviderMap(mapper.src);
|
||||||
|
} else {
|
||||||
|
this.src = mapper.src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object readResolve() throws ObjectStreamException {
|
||||||
|
return new MapperInvocationHandler(iface, src);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,22 +16,25 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import com.datastax.driver.core.Metadata;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.datastax.driver.core.Metadata;
|
|
||||||
import net.helenus.core.DslInstantiator;
|
import net.helenus.core.DslInstantiator;
|
||||||
|
|
||||||
public enum ReflectionDslInstantiator implements DslInstantiator {
|
public enum ReflectionDslInstantiator implements DslInstantiator {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
INSTANCE;
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
public <E> E instantiate(
|
||||||
@SuppressWarnings("unchecked")
|
Class<E> iface,
|
||||||
public <E> E instantiate(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, Metadata metadata) {
|
ClassLoader classLoader,
|
||||||
DslInvocationHandler<E> handler = new DslInvocationHandler<E>(iface, classLoader, parent, metadata);
|
Optional<HelenusPropertyNode> parent,
|
||||||
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, DslExportable.class}, handler);
|
Metadata metadata) {
|
||||||
return proxy;
|
DslInvocationHandler<E> handler =
|
||||||
}
|
new DslInvocationHandler<E>(iface, classLoader, parent, metadata);
|
||||||
|
E proxy =
|
||||||
|
(E) Proxy.newProxyInstance(classLoader, new Class[] {iface, DslExportable.class}, handler);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,17 +20,14 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class ReflectionInstantiator {
|
public final class ReflectionInstantiator {
|
||||||
|
|
||||||
private ReflectionInstantiator() {
|
private ReflectionInstantiator() {}
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T instantiateClass(Class<T> clazz) {
|
public static <T> T instantiateClass(Class<T> clazz) {
|
||||||
|
|
||||||
try {
|
|
||||||
return clazz.newInstance();
|
|
||||||
} catch (InstantiationException | IllegalAccessException e) {
|
|
||||||
throw new HelenusMappingException("invalid class " + clazz, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
return clazz.newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
throw new HelenusMappingException("invalid class " + clazz, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,23 +16,23 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import net.helenus.core.MapperInstantiator;
|
import net.helenus.core.MapperInstantiator;
|
||||||
|
|
||||||
public enum ReflectionMapperInstantiator implements MapperInstantiator {
|
public enum ReflectionMapperInstantiator implements MapperInstantiator {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
INSTANCE;
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
public <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
|
|
||||||
|
|
||||||
MapperInvocationHandler<E> handler = new MapperInvocationHandler<E>(iface, src);
|
|
||||||
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, MapExportable.class}, handler);
|
|
||||||
return proxy;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
MapperInvocationHandler<E> handler = new MapperInvocationHandler<E>(iface, src);
|
||||||
|
E proxy =
|
||||||
|
(E)
|
||||||
|
Proxy.newProxyInstance(
|
||||||
|
classLoader, new Class[] {iface, MapExportable.class, Serializable.class}, handler);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,104 +19,103 @@ package net.helenus.core.reflect;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class SetDsl<V> implements Set<V> {
|
public final class SetDsl<V> implements Set<V> {
|
||||||
|
|
||||||
private final HelenusPropertyNode parent;
|
private final HelenusPropertyNode parent;
|
||||||
|
|
||||||
public SetDsl(HelenusPropertyNode parent) {
|
public SetDsl(HelenusPropertyNode parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusPropertyNode getParent() {
|
public HelenusPropertyNode getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object o) {
|
public boolean contains(Object o) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<V> iterator() {
|
public Iterator<V> iterator() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] toArray() {
|
public Object[] toArray() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T[] toArray(T[] a) {
|
public <T> T[] toArray(T[] a) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(V e) {
|
public boolean add(V e) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(Object o) {
|
public boolean remove(Object o) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsAll(Collection<?> c) {
|
public boolean containsAll(Collection<?> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(Collection<? extends V> c) {
|
public boolean addAll(Collection<? extends V> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean retainAll(Collection<?> c) {
|
public boolean retainAll(Collection<?> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeAll(Collection<?> c) {
|
public boolean removeAll(Collection<?> c) {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
throwShouldNeverCall();
|
throwShouldNeverCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void throwShouldNeverCall() {
|
private void throwShouldNeverCall() {
|
||||||
throw new HelenusMappingException("should be never called");
|
throw new HelenusMappingException("should be never called");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SetDsl";
|
return "SetDsl";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,7 +17,6 @@
|
||||||
package net.helenus.mapping;
|
package net.helenus.mapping;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import net.helenus.mapping.annotation.ClusteringColumn;
|
import net.helenus.mapping.annotation.ClusteringColumn;
|
||||||
import net.helenus.mapping.annotation.Column;
|
import net.helenus.mapping.annotation.Column;
|
||||||
import net.helenus.mapping.annotation.PartitionKey;
|
import net.helenus.mapping.annotation.PartitionKey;
|
||||||
|
@ -25,93 +25,99 @@ import net.helenus.support.HelenusMappingException;
|
||||||
|
|
||||||
public final class ColumnInformation {
|
public final class ColumnInformation {
|
||||||
|
|
||||||
private final IdentityName columnName;
|
private final IdentityName columnName;
|
||||||
private final ColumnType columnType;
|
private final ColumnType columnType;
|
||||||
private final int ordinal;
|
private final int ordinal;
|
||||||
private final OrderingDirection ordering;
|
private final OrderingDirection ordering;
|
||||||
|
|
||||||
public ColumnInformation(Method getter) {
|
public ColumnInformation(Method getter) {
|
||||||
|
|
||||||
String columnName = null;
|
String columnName = null;
|
||||||
boolean forceQuote = false;
|
boolean forceQuote = false;
|
||||||
ColumnType columnTypeLocal = ColumnType.COLUMN;
|
ColumnType columnTypeLocal = ColumnType.COLUMN;
|
||||||
int ordinalLocal = 0;
|
int ordinalLocal = 0;
|
||||||
OrderingDirection orderingLocal = OrderingDirection.ASC;
|
OrderingDirection orderingLocal = OrderingDirection.ASC;
|
||||||
|
|
||||||
PartitionKey partitionKey = getter.getDeclaredAnnotation(PartitionKey.class);
|
PartitionKey partitionKey = getter.getDeclaredAnnotation(PartitionKey.class);
|
||||||
if (partitionKey != null) {
|
if (partitionKey != null) {
|
||||||
columnName = partitionKey.value();
|
columnName = partitionKey.value();
|
||||||
forceQuote = partitionKey.forceQuote();
|
forceQuote = partitionKey.forceQuote();
|
||||||
columnTypeLocal = ColumnType.PARTITION_KEY;
|
columnTypeLocal = ColumnType.PARTITION_KEY;
|
||||||
ordinalLocal = partitionKey.ordinal();
|
ordinalLocal = partitionKey.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClusteringColumn clusteringColumn = getter.getDeclaredAnnotation(ClusteringColumn.class);
|
ClusteringColumn clusteringColumn = getter.getDeclaredAnnotation(ClusteringColumn.class);
|
||||||
if (clusteringColumn != null) {
|
if (clusteringColumn != null) {
|
||||||
ensureSingleColumnType(columnTypeLocal, getter);
|
ensureSingleColumnType(columnTypeLocal, getter);
|
||||||
columnName = clusteringColumn.value();
|
columnName = clusteringColumn.value();
|
||||||
forceQuote = clusteringColumn.forceQuote();
|
forceQuote = clusteringColumn.forceQuote();
|
||||||
columnTypeLocal = ColumnType.CLUSTERING_COLUMN;
|
columnTypeLocal = ColumnType.CLUSTERING_COLUMN;
|
||||||
ordinalLocal = clusteringColumn.ordinal();
|
ordinalLocal = clusteringColumn.ordinal();
|
||||||
orderingLocal = clusteringColumn.ordering();
|
orderingLocal = clusteringColumn.ordering();
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticColumn staticColumn = getter.getDeclaredAnnotation(StaticColumn.class);
|
StaticColumn staticColumn = getter.getDeclaredAnnotation(StaticColumn.class);
|
||||||
if (staticColumn != null) {
|
if (staticColumn != null) {
|
||||||
ensureSingleColumnType(columnTypeLocal, getter);
|
ensureSingleColumnType(columnTypeLocal, getter);
|
||||||
columnName = staticColumn.value();
|
columnName = staticColumn.value();
|
||||||
forceQuote = staticColumn.forceQuote();
|
forceQuote = staticColumn.forceQuote();
|
||||||
columnTypeLocal = ColumnType.STATIC_COLUMN;
|
columnTypeLocal = ColumnType.STATIC_COLUMN;
|
||||||
ordinalLocal = staticColumn.ordinal();
|
ordinalLocal = staticColumn.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
Column column = getter.getDeclaredAnnotation(Column.class);
|
Column column = getter.getDeclaredAnnotation(Column.class);
|
||||||
if (column != null) {
|
if (column != null) {
|
||||||
ensureSingleColumnType(columnTypeLocal, getter);
|
ensureSingleColumnType(columnTypeLocal, getter);
|
||||||
columnName = column.value();
|
columnName = column.value();
|
||||||
forceQuote = column.forceQuote();
|
forceQuote = column.forceQuote();
|
||||||
columnTypeLocal = ColumnType.COLUMN;
|
columnTypeLocal = ColumnType.COLUMN;
|
||||||
ordinalLocal = column.ordinal();
|
ordinalLocal = column.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnName == null || columnName.isEmpty()) {
|
if (columnName == null || columnName.isEmpty()) {
|
||||||
columnName = MappingUtil.getDefaultColumnName(getter);
|
columnName = MappingUtil.getDefaultColumnName(getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.columnName = new IdentityName(columnName, forceQuote);
|
this.columnName = new IdentityName(columnName, forceQuote);
|
||||||
this.columnType = columnTypeLocal;
|
this.columnType = columnTypeLocal;
|
||||||
this.ordinal = ordinalLocal;
|
this.ordinal = ordinalLocal;
|
||||||
this.ordering = orderingLocal;
|
this.ordering = orderingLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityName getColumnName() {
|
public IdentityName getColumnName() {
|
||||||
return columnName;
|
return columnName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColumnType getColumnType() {
|
public ColumnType getColumnType() {
|
||||||
return columnType;
|
return columnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOrdinal() {
|
public int getOrdinal() {
|
||||||
return ordinal;
|
return ordinal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrderingDirection getOrdering() {
|
public OrderingDirection getOrdering() {
|
||||||
return ordering;
|
return ordering;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureSingleColumnType(ColumnType columnTypeLocal, Method getter) {
|
private void ensureSingleColumnType(ColumnType columnTypeLocal, Method getter) {
|
||||||
|
|
||||||
if (columnTypeLocal != ColumnType.COLUMN) {
|
if (columnTypeLocal != ColumnType.COLUMN) {
|
||||||
throw new HelenusMappingException("property can be annotated only by a single column type " + getter);
|
throw new HelenusMappingException(
|
||||||
}
|
"property can be annotated only by a single column type " + getter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ColumnInformation [columnName=" + columnName + ", columnType=" + columnType + ", ordinal=" + ordinal
|
|
||||||
+ ", ordering=" + ordering + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ColumnInformation [columnName="
|
||||||
|
+ columnName
|
||||||
|
+ ", columnType="
|
||||||
|
+ columnType
|
||||||
|
+ ", ordinal="
|
||||||
|
+ ordinal
|
||||||
|
+ ", ordering="
|
||||||
|
+ ordering
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,5 +17,8 @@
|
||||||
package net.helenus.mapping;
|
package net.helenus.mapping;
|
||||||
|
|
||||||
public enum ColumnType {
|
public enum ColumnType {
|
||||||
PARTITION_KEY, CLUSTERING_COLUMN, STATIC_COLUMN, COLUMN;
|
PARTITION_KEY,
|
||||||
|
CLUSTERING_COLUMN,
|
||||||
|
STATIC_COLUMN,
|
||||||
|
COLUMN;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,19 +17,24 @@
|
||||||
package net.helenus.mapping;
|
package net.helenus.mapping;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
|
||||||
public interface HelenusEntity {
|
public interface HelenusEntity {
|
||||||
|
|
||||||
HelenusEntityType getType();
|
HelenusEntityType getType();
|
||||||
|
|
||||||
boolean isCacheable();
|
boolean isCacheable();
|
||||||
|
|
||||||
Class<?> getMappingInterface();
|
Class<?> getMappingInterface();
|
||||||
|
|
||||||
IdentityName getName();
|
IdentityName getName();
|
||||||
|
|
||||||
Collection<HelenusProperty> getOrderedProperties();
|
Collection<HelenusProperty> getOrderedProperties();
|
||||||
|
|
||||||
HelenusProperty getProperty(String name);
|
HelenusProperty getProperty(String name);
|
||||||
|
|
||||||
|
List<Facet> getFacets();
|
||||||
|
|
||||||
|
boolean isDraftable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,5 +17,8 @@
|
||||||
package net.helenus.mapping;
|
package net.helenus.mapping;
|
||||||
|
|
||||||
public enum HelenusEntityType {
|
public enum HelenusEntityType {
|
||||||
TABLE, TUPLE, UDT;
|
TABLE,
|
||||||
|
VIEW,
|
||||||
|
TUPLE,
|
||||||
|
UDT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 The Helenus Authors
|
* Copyright (C) 2015 The Casser Authors
|
||||||
|
* Copyright (C) 2015-2018 The Helenus Authors
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
|
@ -15,256 +16,356 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.mapping;
|
package net.helenus.mapping;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import com.datastax.driver.core.DefaultMetadata;
|
||||||
import java.util.*;
|
import com.datastax.driver.core.Metadata;
|
||||||
|
|
||||||
import com.datastax.driver.core.*;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import java.util.*;
|
||||||
import com.google.common.reflect.TypeToken;
|
import javax.validation.ConstraintValidator;
|
||||||
import net.helenus.config.HelenusSettings;
|
import net.helenus.config.HelenusSettings;
|
||||||
import net.helenus.core.Helenus;
|
import net.helenus.core.Helenus;
|
||||||
import net.helenus.core.annotation.Cacheable;
|
import net.helenus.core.annotation.Cacheable;
|
||||||
|
import net.helenus.core.cache.Facet;
|
||||||
|
import net.helenus.core.cache.UnboundFacet;
|
||||||
import net.helenus.mapping.annotation.*;
|
import net.helenus.mapping.annotation.*;
|
||||||
|
import net.helenus.mapping.validator.DistinctValidator;
|
||||||
import net.helenus.support.HelenusMappingException;
|
import net.helenus.support.HelenusMappingException;
|
||||||
|
import org.apache.commons.lang3.ClassUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public final class HelenusMappingEntity implements HelenusEntity {
|
public final class HelenusMappingEntity implements HelenusEntity {
|
||||||
|
|
||||||
private final Class<?> iface;
|
private final Class<?> iface;
|
||||||
private final HelenusEntityType type;
|
private final HelenusEntityType type;
|
||||||
private final IdentityName name;
|
private final IdentityName name;
|
||||||
private final boolean cacheable;
|
private final boolean cacheable;
|
||||||
private final ImmutableMap<String, Method> methods;
|
private final boolean draftable;
|
||||||
private final ImmutableMap<String, HelenusProperty> props;
|
private final ImmutableMap<String, Method> methods;
|
||||||
private final ImmutableList<HelenusProperty> orderedProps;
|
private final ImmutableMap<String, HelenusProperty> props;
|
||||||
|
private final ImmutableList<HelenusProperty> orderedProps;
|
||||||
|
private final List<Facet> facets;
|
||||||
|
|
||||||
public HelenusMappingEntity(Class<?> iface, Metadata metadata) {
|
public HelenusMappingEntity(Class<?> iface, Metadata metadata) {
|
||||||
this(iface, autoDetectType(iface), metadata);
|
this(iface, autoDetectType(iface), metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HelenusMappingEntity(Class<?> iface, HelenusEntityType type, Metadata metadata) {
|
public HelenusMappingEntity(Class<?> iface, HelenusEntityType type, Metadata metadata) {
|
||||||
|
|
||||||
if (iface == null || !iface.isInterface()) {
|
if (iface == null || !iface.isInterface()) {
|
||||||
throw new IllegalArgumentException("invalid parameter " + iface);
|
throw new IllegalArgumentException("invalid parameter " + iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.iface = iface;
|
this.iface = iface;
|
||||||
this.type = Objects.requireNonNull(type, "type is empty");
|
this.type = Objects.requireNonNull(type, "type is empty");
|
||||||
this.name = resolveName(iface, type);
|
this.name = resolveName(iface, type);
|
||||||
|
|
||||||
HelenusSettings settings = Helenus.settings();
|
HelenusSettings settings = Helenus.settings();
|
||||||
|
|
||||||
|
Map<String, Method> methods = new HashMap<String, Method>();
|
||||||
|
for (Method m : iface.getDeclaredMethods()) {
|
||||||
|
methods.put(m.getName(), m);
|
||||||
|
}
|
||||||
|
|
||||||
List<Method> methods = new ArrayList<Method>();
|
for (Class<?> c : ClassUtils.getAllInterfaces(iface)) {
|
||||||
|
if (c.getDeclaredAnnotation(Table.class) != null
|
||||||
methods.addAll(Arrays.asList(iface.getDeclaredMethods()));
|
|| c.getDeclaredAnnotation(InheritedTable.class) != null) {
|
||||||
for (Class<?> c : iface.getInterfaces()) {
|
for (Method m : c.getDeclaredMethods()) {
|
||||||
methods.addAll(Arrays.asList(c.getDeclaredMethods()));
|
Method o = methods.get(m.getName());
|
||||||
|
if (o != null) {
|
||||||
|
// Prefer overridden method implementation.
|
||||||
|
if (o.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) {
|
||||||
|
methods.put(m.getName(), m);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
methods.put(m.getName(), m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<HelenusProperty> propsLocal = new ArrayList<HelenusProperty>();
|
List<HelenusProperty> propsLocal = new ArrayList<HelenusProperty>();
|
||||||
ImmutableMap.Builder<String, HelenusProperty> propsBuilder = ImmutableMap.builder();
|
ImmutableMap.Builder<String, HelenusProperty> propsBuilder = ImmutableMap.builder();
|
||||||
ImmutableMap.Builder<String, Method> methodsBuilder = ImmutableMap.builder();
|
ImmutableMap.Builder<String, Method> methodsBuilder = ImmutableMap.builder();
|
||||||
|
|
||||||
for (Method method : methods) {
|
for (Method method : methods.values()) {
|
||||||
|
|
||||||
if (settings.getGetterMethodDetector().apply(method)) {
|
if (settings.getGetterMethodDetector().apply(method)) {
|
||||||
|
|
||||||
methodsBuilder.put(method.getName(), method);
|
methodsBuilder.put(method.getName(), method);
|
||||||
|
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
HelenusProperty prop = new HelenusMappingProperty(this, method, metadata);
|
HelenusProperty prop = new HelenusMappingProperty(this, method, metadata);
|
||||||
|
|
||||||
propsBuilder.put(prop.getPropertyName(), prop);
|
propsBuilder.put(prop.getPropertyName(), prop);
|
||||||
propsLocal.add(prop);
|
propsLocal.add(prop);
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this.methods = methodsBuilder.build();
|
|
||||||
this.props = propsBuilder.build();
|
|
||||||
|
|
||||||
Collections.sort(propsLocal, TypeAndOrdinalColumnComparator.INSTANCE);
|
|
||||||
this.orderedProps = ImmutableList.copyOf(propsLocal);
|
|
||||||
|
|
||||||
validateOrdinals();
|
|
||||||
|
|
||||||
cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HelenusEntityType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCacheable() {
|
|
||||||
return cacheable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getMappingInterface() {
|
|
||||||
return iface;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<HelenusProperty> getOrderedProperties() {
|
|
||||||
return orderedProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HelenusProperty getProperty(String name) {
|
|
||||||
HelenusProperty property = props.get(name);
|
|
||||||
if (property == null && methods.containsKey(name)) {
|
|
||||||
property = new HelenusMappingProperty(this, methods.get(name), new DefaultMetadata());
|
|
||||||
return property; //TODO(gburd): review adding these into the props map...
|
|
||||||
}
|
}
|
||||||
return props.get(name);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
this.methods = methodsBuilder.build();
|
||||||
public IdentityName getName() {
|
this.props = propsBuilder.build();
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IdentityName resolveName(Class<?> iface, HelenusEntityType type) {
|
Collections.sort(propsLocal, TypeAndOrdinalColumnComparator.INSTANCE);
|
||||||
|
this.orderedProps = ImmutableList.copyOf(propsLocal);
|
||||||
|
|
||||||
switch (type) {
|
validateOrdinals();
|
||||||
|
|
||||||
case TABLE :
|
// Caching
|
||||||
return MappingUtil.getTableName(iface, true);
|
cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class));
|
||||||
|
|
||||||
case TUPLE :
|
// Draft
|
||||||
return IdentityName.of(MappingUtil.getDefaultEntityName(iface), false);
|
Class<?> draft;
|
||||||
|
try {
|
||||||
|
draft = Class.forName(iface.getName() + "$Draft");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
draft = null;
|
||||||
|
}
|
||||||
|
draftable = (draft != null);
|
||||||
|
|
||||||
case UDT :
|
// Materialized view
|
||||||
return MappingUtil.getUserDefinedTypeName(iface, true);
|
List<HelenusProperty> primaryKeyProperties = new ArrayList<>();
|
||||||
}
|
ImmutableList.Builder<Facet> facetsBuilder = ImmutableList.builder();
|
||||||
|
if (iface.getDeclaredAnnotation(MaterializedView.class) == null) {
|
||||||
|
facetsBuilder.add(new Facet("table", name.toCql()).setFixed());
|
||||||
|
} else {
|
||||||
|
facetsBuilder.add(
|
||||||
|
new Facet("table", Helenus.entity(iface.getInterfaces()[0]).getName().toCql())
|
||||||
|
.setFixed());
|
||||||
|
}
|
||||||
|
for (HelenusProperty prop : orderedProps) {
|
||||||
|
switch (prop.getColumnType()) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
case CLUSTERING_COLUMN:
|
||||||
|
primaryKeyProperties.add(prop);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (primaryKeyProperties != null && primaryKeyProperties.size() > 0) {
|
||||||
|
facetsBuilder.add(new UnboundFacet(primaryKeyProperties));
|
||||||
|
primaryKeyProperties = null;
|
||||||
|
}
|
||||||
|
for (ConstraintValidator<?, ?> constraint :
|
||||||
|
MappingUtil.getValidators(prop.getGetterMethod())) {
|
||||||
|
if (constraint instanceof DistinctValidator) {
|
||||||
|
DistinctValidator validator = (DistinctValidator) constraint;
|
||||||
|
String[] values = validator.constraintAnnotation.value();
|
||||||
|
UnboundFacet facet;
|
||||||
|
if (values != null && values.length >= 1 && !(StringUtils.isBlank(values[0]))) {
|
||||||
|
List<HelenusProperty> props = new ArrayList<HelenusProperty>(values.length + 1);
|
||||||
|
props.add(prop);
|
||||||
|
for (String value : values) {
|
||||||
|
for (HelenusProperty p : orderedProps) {
|
||||||
|
String name = p.getPropertyName();
|
||||||
|
if (name.equals(value) && !name.equals(prop.getPropertyName())) {
|
||||||
|
props.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
facet = new UnboundFacet(props, validator.alone(), validator.combined());
|
||||||
|
} else {
|
||||||
|
facet = new UnboundFacet(prop, validator.alone(), validator.combined());
|
||||||
|
}
|
||||||
|
facetsBuilder.add(facet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (primaryKeyProperties != null && primaryKeyProperties.size() > 0) {
|
||||||
|
facetsBuilder.add(new UnboundFacet(primaryKeyProperties));
|
||||||
|
}
|
||||||
|
this.facets = facetsBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
throw new HelenusMappingException("invalid entity type " + type + " in " + type);
|
private static IdentityName resolveName(Class<?> iface, HelenusEntityType type) {
|
||||||
|
|
||||||
}
|
switch (type) {
|
||||||
|
case TABLE:
|
||||||
|
return MappingUtil.getTableName(iface, true);
|
||||||
|
|
||||||
private static HelenusEntityType autoDetectType(Class<?> iface) {
|
case VIEW:
|
||||||
|
return MappingUtil.getViewName(iface, true);
|
||||||
|
|
||||||
Objects.requireNonNull(iface, "empty iface");
|
case TUPLE:
|
||||||
|
return IdentityName.of(MappingUtil.getDefaultEntityName(iface), false);
|
||||||
|
|
||||||
if (null != iface.getDeclaredAnnotation(Table.class)) {
|
case UDT:
|
||||||
return HelenusEntityType.TABLE;
|
return MappingUtil.getUserDefinedTypeName(iface, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (null != iface.getDeclaredAnnotation(Tuple.class)) {
|
throw new HelenusMappingException("invalid entity type " + type + " in " + type);
|
||||||
return HelenusEntityType.TUPLE;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
else if (null != iface.getDeclaredAnnotation(UDT.class)) {
|
private static HelenusEntityType autoDetectType(Class<?> iface) {
|
||||||
return HelenusEntityType.UDT;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HelenusMappingException("entity must be annotated by @Table or @Tuple or @UserDefinedType " + iface);
|
Objects.requireNonNull(iface, "empty iface");
|
||||||
}
|
|
||||||
|
|
||||||
private void validateOrdinals() {
|
if (null != iface.getDeclaredAnnotation(Table.class)) {
|
||||||
|
return HelenusEntityType.TABLE;
|
||||||
|
} else if (null != iface.getDeclaredAnnotation(MaterializedView.class)) {
|
||||||
|
return HelenusEntityType.VIEW;
|
||||||
|
} else if (null != iface.getDeclaredAnnotation(Tuple.class)) {
|
||||||
|
return HelenusEntityType.TUPLE;
|
||||||
|
} else if (null != iface.getDeclaredAnnotation(UDT.class)) {
|
||||||
|
return HelenusEntityType.UDT;
|
||||||
|
}
|
||||||
|
|
||||||
switch (getType()) {
|
throw new HelenusMappingException(
|
||||||
|
"entity must be annotated by @Table or @Tuple or @UserDefinedType " + iface);
|
||||||
|
}
|
||||||
|
|
||||||
case TABLE :
|
@Override
|
||||||
validateOrdinalsForTable();
|
public HelenusEntityType getType() {
|
||||||
break;
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
case TUPLE :
|
@Override
|
||||||
validateOrdinalsInTuple();
|
public boolean isCacheable() {
|
||||||
break;
|
return cacheable;
|
||||||
|
}
|
||||||
|
|
||||||
default :
|
@Override
|
||||||
break;
|
public boolean isDraftable() {
|
||||||
}
|
return draftable;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public Class<?> getMappingInterface() {
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
private void validateOrdinalsForTable() {
|
@Override
|
||||||
|
public Collection<HelenusProperty> getOrderedProperties() {
|
||||||
|
return orderedProps;
|
||||||
|
}
|
||||||
|
|
||||||
BitSet partitionKeys = new BitSet();
|
@Override
|
||||||
BitSet clusteringColumns = new BitSet();
|
public HelenusProperty getProperty(String name) {
|
||||||
|
HelenusProperty property = props.get(name);
|
||||||
|
if (property == null && methods.containsKey(name)) {
|
||||||
|
property = new HelenusMappingProperty(this, methods.get(name), new DefaultMetadata());
|
||||||
|
return property; // TODO(gburd): review adding these into the props map...
|
||||||
|
}
|
||||||
|
return props.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
for (HelenusProperty prop : getOrderedProperties()) {
|
@Override
|
||||||
|
public List<Facet> getFacets() {
|
||||||
|
return facets;
|
||||||
|
}
|
||||||
|
|
||||||
ColumnType type = prop.getColumnType();
|
@Override
|
||||||
|
public IdentityName getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
int ordinal = prop.getOrdinal();
|
private void validateOrdinals() {
|
||||||
|
|
||||||
switch (type) {
|
switch (getType()) {
|
||||||
|
case TABLE:
|
||||||
|
validateOrdinalsForTable();
|
||||||
|
break;
|
||||||
|
|
||||||
case PARTITION_KEY :
|
case TUPLE:
|
||||||
if (partitionKeys.get(ordinal)) {
|
validateOrdinalsInTuple();
|
||||||
throw new HelenusMappingException(
|
break;
|
||||||
"detected two or more partition key columns with the same ordinal " + ordinal + " in "
|
|
||||||
+ prop.getEntity());
|
|
||||||
}
|
|
||||||
partitionKeys.set(ordinal);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLUSTERING_COLUMN :
|
default:
|
||||||
if (clusteringColumns.get(ordinal)) {
|
break;
|
||||||
throw new HelenusMappingException("detected two or clustering columns with the same ordinal "
|
}
|
||||||
+ ordinal + " in " + prop.getEntity());
|
}
|
||||||
}
|
|
||||||
clusteringColumns.set(ordinal);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default :
|
private void validateOrdinalsForTable() {
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
BitSet partitionKeys = new BitSet();
|
||||||
|
BitSet clusteringColumns = new BitSet();
|
||||||
|
|
||||||
}
|
for (HelenusProperty prop : getOrderedProperties()) {
|
||||||
|
|
||||||
private void validateOrdinalsInTuple() {
|
ColumnType type = prop.getColumnType();
|
||||||
boolean[] ordinals = new boolean[props.size()];
|
|
||||||
|
|
||||||
getOrderedProperties().forEach(p -> {
|
int ordinal = prop.getOrdinal();
|
||||||
|
|
||||||
int ordinal = p.getOrdinal();
|
switch (type) {
|
||||||
|
case PARTITION_KEY:
|
||||||
|
if (partitionKeys.get(ordinal)) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"detected two or more partition key columns with the same ordinal "
|
||||||
|
+ ordinal
|
||||||
|
+ " in "
|
||||||
|
+ prop.getEntity());
|
||||||
|
}
|
||||||
|
partitionKeys.set(ordinal);
|
||||||
|
break;
|
||||||
|
|
||||||
if (ordinal < 0 || ordinal >= ordinals.length) {
|
case CLUSTERING_COLUMN:
|
||||||
throw new HelenusMappingException("invalid ordinal " + ordinal + " found for property "
|
if (clusteringColumns.get(ordinal)) {
|
||||||
+ p.getPropertyName() + " in " + p.getEntity());
|
throw new HelenusMappingException(
|
||||||
}
|
"detected two or clustering columns with the same ordinal "
|
||||||
|
+ ordinal
|
||||||
|
+ " in "
|
||||||
|
+ prop.getEntity());
|
||||||
|
}
|
||||||
|
clusteringColumns.set(ordinal);
|
||||||
|
break;
|
||||||
|
|
||||||
if (ordinals[ordinal]) {
|
default:
|
||||||
throw new HelenusMappingException(
|
break;
|
||||||
"detected two or more properties with the same ordinal " + ordinal + " in " + p.getEntity());
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ordinals[ordinal] = true;
|
private void validateOrdinalsInTuple() {
|
||||||
|
boolean[] ordinals = new boolean[props.size()];
|
||||||
|
|
||||||
});
|
getOrderedProperties()
|
||||||
|
.forEach(
|
||||||
|
p -> {
|
||||||
|
int ordinal = p.getOrdinal();
|
||||||
|
|
||||||
for (int i = 0; i != ordinals.length; ++i) {
|
if (ordinal < 0 || ordinal >= ordinals.length) {
|
||||||
if (!ordinals[i]) {
|
throw new HelenusMappingException(
|
||||||
throw new HelenusMappingException("detected absent ordinal " + i + " in " + this);
|
"invalid ordinal "
|
||||||
}
|
+ ordinal
|
||||||
}
|
+ " found for property "
|
||||||
|
+ p.getPropertyName()
|
||||||
|
+ " in "
|
||||||
|
+ p.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
if (ordinals[ordinal]) {
|
||||||
|
throw new HelenusMappingException(
|
||||||
|
"detected two or more properties with the same ordinal "
|
||||||
|
+ ordinal
|
||||||
|
+ " in "
|
||||||
|
+ p.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
ordinals[ordinal] = true;
|
||||||
public String toString() {
|
});
|
||||||
|
|
||||||
StringBuilder str = new StringBuilder();
|
for (int i = 0; i != ordinals.length; ++i) {
|
||||||
str.append(iface.getSimpleName()).append("(").append(name.getName()).append(") ")
|
if (!ordinals[i]) {
|
||||||
.append(type.name().toLowerCase()).append(":\n");
|
throw new HelenusMappingException("detected absent ordinal " + i + " in " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (HelenusProperty prop : getOrderedProperties()) {
|
@Override
|
||||||
str.append(prop.toString());
|
public String toString() {
|
||||||
str.append("\n");
|
|
||||||
}
|
|
||||||
return str.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
str.append(iface.getSimpleName())
|
||||||
|
.append("(")
|
||||||
|
.append(name.getName())
|
||||||
|
.append(") ")
|
||||||
|
.append(type.name().toLowerCase())
|
||||||
|
.append(":\n");
|
||||||
|
|
||||||
|
for (HelenusProperty prop : getOrderedProperties()) {
|
||||||
|
str.append(prop.toString());
|
||||||
|
str.append("\n");
|
||||||
|
}
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue