begin user types implementation
This commit is contained in:
parent
5242b0ce75
commit
3100cce682
11 changed files with 282 additions and 121 deletions
|
@ -40,7 +40,7 @@ public interface Timeline {
|
|||
Session initialization:
|
||||
```
|
||||
Timeline timeline = Casser.dsl(Timeline.class);
|
||||
CasserSession session = Casser.init(getSession()).showCql().createDrop(Timeline.class).get();
|
||||
CasserSession session = Casser.init(getSession()).showCql().add(Timeline.class).autoCreateDrop().get();
|
||||
```
|
||||
|
||||
Select information:
|
||||
|
|
|
@ -31,7 +31,7 @@ public class Example {
|
|||
|
||||
Cluster cluster = new Cluster.Builder().addContactPoint("localhost").build();
|
||||
|
||||
CasserSession session = Casser.connect(cluster).use("test").update(_user).get();
|
||||
CasserSession session = Casser.connect(cluster).use("test").add(_user).autoUpdate().get();
|
||||
|
||||
public static User mapUser(Tuple2<String, Integer> t) {
|
||||
User user = Casser.pojo(User.class);
|
||||
|
|
23
src/main/java/casser/core/AutoDsl.java
Normal file
23
src/main/java/casser/core/AutoDsl.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Noorq, Inc.
|
||||
*
|
||||
* 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 casser.core;
|
||||
|
||||
public enum AutoDsl {
|
||||
VALIDATE,
|
||||
UPDATE,
|
||||
CREATE,
|
||||
CREATE_DROP;
|
||||
}
|
|
@ -17,7 +17,6 @@ package casser.core;
|
|||
|
||||
import java.io.Closeable;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import casser.core.dsl.Getter;
|
||||
|
@ -47,22 +46,22 @@ public class CasserSession extends AbstractSessionOperations implements Closeabl
|
|||
private final Session session;
|
||||
private volatile String usingKeyspace;
|
||||
private volatile boolean showCql;
|
||||
private final Set<CasserMappingEntity<?>> dropEntitiesOnClose;
|
||||
private final CasserMappingRepository mappingRepository;
|
||||
private final Executor executor;
|
||||
private final boolean dropSchemaOnClose;
|
||||
|
||||
CasserSession(Session session,
|
||||
String usingKeyspace,
|
||||
boolean showCql,
|
||||
Set<CasserMappingEntity<?>> dropEntitiesOnClose,
|
||||
CasserMappingRepository mappingRepository,
|
||||
Executor executor) {
|
||||
Executor executor,
|
||||
boolean dropSchemaOnClose) {
|
||||
this.session = session;
|
||||
this.usingKeyspace = Objects.requireNonNull(usingKeyspace, "keyspace needs to be selected before creating session");
|
||||
this.showCql = showCql;
|
||||
this.dropEntitiesOnClose = dropEntitiesOnClose;
|
||||
this.mappingRepository = mappingRepository;
|
||||
this.mappingRepository = mappingRepository.setReadOnly();
|
||||
this.executor = executor;
|
||||
this.dropSchemaOnClose = dropSchemaOnClose;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -243,24 +242,30 @@ public class CasserSession extends AbstractSessionOperations implements Closeabl
|
|||
}
|
||||
|
||||
public void close() {
|
||||
dropEntitiesIfNeeded();
|
||||
|
||||
if (session.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropSchemaOnClose) {
|
||||
dropSchema();
|
||||
}
|
||||
|
||||
session.close();
|
||||
}
|
||||
|
||||
public CloseFuture closeAsync() {
|
||||
dropEntitiesIfNeeded();
|
||||
|
||||
if (!session.isClosed() && dropSchemaOnClose) {
|
||||
dropSchema();
|
||||
}
|
||||
|
||||
return session.closeAsync();
|
||||
}
|
||||
|
||||
private void dropEntitiesIfNeeded() {
|
||||
private void dropSchema() {
|
||||
|
||||
if (dropEntitiesOnClose == null || dropEntitiesOnClose.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (CasserMappingEntity<?> entity : dropEntitiesOnClose) {
|
||||
dropEntity(entity);
|
||||
}
|
||||
mappingRepository.getKnownEntities().forEach(e -> dropEntity(e));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -15,21 +15,25 @@
|
|||
*/
|
||||
package casser.core;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import casser.core.tuple.Tuple2;
|
||||
import casser.mapping.CasserMappingEntity;
|
||||
import casser.mapping.CasserMappingRepository;
|
||||
import casser.mapping.MappingUtil;
|
||||
import casser.support.CasserException;
|
||||
import casser.support.Requires;
|
||||
import casser.mapping.SimpleDataTypes;
|
||||
import casser.support.CasserMappingException;
|
||||
|
||||
import com.datastax.driver.core.KeyspaceMetadata;
|
||||
import com.datastax.driver.core.Session;
|
||||
import com.datastax.driver.core.TableMetadata;
|
||||
import com.datastax.driver.core.UserType;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
|
||||
|
@ -39,7 +43,6 @@ public class SessionInitializer extends AbstractSessionOperations {
|
|||
private String usingKeyspace;
|
||||
private boolean showCql = false;
|
||||
private Executor executor = MoreExecutors.sameThreadExecutor();
|
||||
private Set<CasserMappingEntity<?>> dropEntitiesOnClose = null;
|
||||
|
||||
private CasserMappingRepository mappingRepository = new CasserMappingRepository();
|
||||
|
||||
|
@ -47,6 +50,9 @@ public class SessionInitializer extends AbstractSessionOperations {
|
|||
|
||||
private KeyspaceMetadata keyspaceMetadata;
|
||||
|
||||
private final List<Object> initList = new ArrayList<Object>();
|
||||
private AutoDsl autoDsl = AutoDsl.UPDATE;
|
||||
|
||||
SessionInitializer(Session session) {
|
||||
this.session = Objects.requireNonNull(session, "empty session");
|
||||
this.usingKeyspace = session.getLoggedKeyspace(); // can be null
|
||||
|
@ -98,23 +104,38 @@ public class SessionInitializer extends AbstractSessionOperations {
|
|||
return showCql;
|
||||
}
|
||||
|
||||
public SessionInitializer validate(Object... dsls) {
|
||||
initialize(AutoDsl.VALIDATE, dsls);
|
||||
public SessionInitializer add(Object... dsls) {
|
||||
Objects.requireNonNull(dsls, "dsls is empty");
|
||||
int len = dsls.length;
|
||||
for (int i = 0; i != len; ++i) {
|
||||
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
|
||||
initList.add(obj);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SessionInitializer update(Object... dsls) {
|
||||
initialize(AutoDsl.UPDATE, dsls);
|
||||
public SessionInitializer autoValidate() {
|
||||
this.autoDsl = AutoDsl.VALIDATE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SessionInitializer create(Object... dsls) {
|
||||
initialize(AutoDsl.CREATE, dsls);
|
||||
public SessionInitializer autoUpdate() {
|
||||
this.autoDsl = AutoDsl.UPDATE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SessionInitializer createDrop(Object... dsls) {
|
||||
initialize(AutoDsl.CREATE_DROP, dsls);
|
||||
public SessionInitializer autoCreate() {
|
||||
this.autoDsl = AutoDsl.CREATE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SessionInitializer autoCreateDrop() {
|
||||
this.autoDsl = AutoDsl.CREATE_DROP;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SessionInitializer auto(AutoDsl autoDsl) {
|
||||
this.autoDsl = autoDsl;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -131,75 +152,71 @@ public class SessionInitializer extends AbstractSessionOperations {
|
|||
}
|
||||
|
||||
public CasserSession get() {
|
||||
initialize();
|
||||
return new CasserSession(session,
|
||||
usingKeyspace,
|
||||
showCql,
|
||||
dropEntitiesOnClose,
|
||||
mappingRepository,
|
||||
executor);
|
||||
executor,
|
||||
autoDsl == AutoDsl.CREATE_DROP);
|
||||
}
|
||||
|
||||
private enum AutoDsl {
|
||||
VALIDATE,
|
||||
UPDATE,
|
||||
CREATE,
|
||||
CREATE_DROP;
|
||||
}
|
||||
|
||||
private void initialize(AutoDsl type, Object[] dsls) {
|
||||
private void initialize() {
|
||||
|
||||
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
|
||||
Requires.nonNullArray(dsls);
|
||||
|
||||
KeyspaceMetadata keyspaceMetadata = getKeyspaceMetadata();
|
||||
initList.forEach(e -> mappingRepository.addEntity(e));
|
||||
|
||||
mappingRepository.addEntities(dsls);
|
||||
Map<String, Class<?>> map = collectUserDefinedTypes();
|
||||
|
||||
TableOperations tableOps = new TableOperations(this, dropRemovedColumns);
|
||||
UserTypeOperations userTypeOps = new UserTypeOperations(this);
|
||||
|
||||
switch(autoDsl) {
|
||||
|
||||
case CREATE:
|
||||
case CREATE_DROP:
|
||||
mappingRepository.getKnownEntities()
|
||||
.forEach(e -> tableOps.createTable(e));
|
||||
break;
|
||||
|
||||
case VALIDATE:
|
||||
mappingRepository.getKnownEntities()
|
||||
.forEach(e -> tableOps.validateTable(getTableMetadata(e), e));
|
||||
break;
|
||||
|
||||
case UPDATE:
|
||||
mappingRepository.getKnownEntities()
|
||||
.forEach(e -> tableOps.updateTable(getTableMetadata(e), e));
|
||||
break;
|
||||
|
||||
for (CasserMappingEntity<?> entity : mappingRepository.getKnownEntities()) {
|
||||
initializeTable(type, entity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void initializeTable(AutoDsl type, CasserMappingEntity<?> entity) {
|
||||
|
||||
if (type == AutoDsl.CREATE || type == AutoDsl.CREATE_DROP) {
|
||||
createNewTable(entity);
|
||||
}
|
||||
else {
|
||||
TableMetadata tmd = getTableMetadata(entity);
|
||||
|
||||
if (type == AutoDsl.VALIDATE) {
|
||||
private Map<String, Class<?>> collectUserDefinedTypes() {
|
||||
|
||||
if (tmd == null) {
|
||||
throw new CasserException("table not exists " + entity.getTableName() + "for entity " + entity.getMappingInterface());
|
||||
}
|
||||
Map<String, Class<?>> map = new HashMap<String, Class<?>>();
|
||||
|
||||
validateTable(tmd, entity);
|
||||
mappingRepository.getKnownEntities().stream()
|
||||
.flatMap(e -> e.getMappingProperties().stream())
|
||||
.map(p -> p.getJavaType())
|
||||
.filter(c -> SimpleDataTypes.getDataTypeByJavaClass(c) == null)
|
||||
.map(c -> new Tuple2<String, Class<?>>(MappingUtil.getUserDefinedTypeName(c), c))
|
||||
.filter(t -> t.v1 != null)
|
||||
.forEach(t -> {
|
||||
|
||||
Class<?> old = map.putIfAbsent(t.v1, t.v2);
|
||||
if (old != null) {
|
||||
throw new CasserMappingException("found UserDefinedType " + t.v1 + " in two classes " + old + " and " + t.v2);
|
||||
}
|
||||
else if (type == AutoDsl.UPDATE) {
|
||||
|
||||
if (tmd == null) {
|
||||
createNewTable(entity);
|
||||
}
|
||||
else {
|
||||
alterTable(tmd, entity);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (type == AutoDsl.CREATE_DROP) {
|
||||
getOrCreateDropEntitiesSet().add(entity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Set<CasserMappingEntity<?>> getOrCreateDropEntitiesSet() {
|
||||
if (dropEntitiesOnClose == null) {
|
||||
dropEntitiesOnClose = new HashSet<CasserMappingEntity<?>>();
|
||||
}
|
||||
return dropEntitiesOnClose;
|
||||
return map;
|
||||
}
|
||||
|
||||
private KeyspaceMetadata getKeyspaceMetadata() {
|
||||
|
@ -215,29 +232,7 @@ public class SessionInitializer extends AbstractSessionOperations {
|
|||
|
||||
}
|
||||
|
||||
private void createNewTable(CasserMappingEntity<?> entity) {
|
||||
|
||||
String cql = SchemaUtil.createTableCql(entity);
|
||||
|
||||
execute(cql);
|
||||
|
||||
}
|
||||
|
||||
private void validateTable(TableMetadata tmd, CasserMappingEntity<?> entity) {
|
||||
|
||||
String cql = SchemaUtil.alterTableCql(tmd, entity, dropRemovedColumns);
|
||||
|
||||
if (cql != null) {
|
||||
throw new CasserException("schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + cql);
|
||||
}
|
||||
}
|
||||
|
||||
private void alterTable(TableMetadata tmd, CasserMappingEntity<?> entity) {
|
||||
|
||||
String cql = SchemaUtil.alterTableCql(tmd, entity, dropRemovedColumns);
|
||||
|
||||
if (cql != null) {
|
||||
execute(cql);
|
||||
}
|
||||
private UserType getUserType(String name) {
|
||||
return getKeyspaceMetadata().getUserType(name.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
|
67
src/main/java/casser/core/TableOperations.java
Normal file
67
src/main/java/casser/core/TableOperations.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Noorq, Inc.
|
||||
*
|
||||
* 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 casser.core;
|
||||
|
||||
import casser.mapping.CasserMappingEntity;
|
||||
import casser.support.CasserException;
|
||||
|
||||
import com.datastax.driver.core.TableMetadata;
|
||||
|
||||
public final class TableOperations {
|
||||
|
||||
private final AbstractSessionOperations sessionOps;
|
||||
private final boolean dropRemovedColumns;
|
||||
|
||||
public TableOperations(AbstractSessionOperations sessionOps, boolean dropRemovedColumns) {
|
||||
this.sessionOps = sessionOps;
|
||||
this.dropRemovedColumns = dropRemovedColumns;
|
||||
}
|
||||
|
||||
public void createTable(CasserMappingEntity<?> entity) {
|
||||
|
||||
String cql = SchemaUtil.createTableCql(entity);
|
||||
|
||||
sessionOps.execute(cql);
|
||||
|
||||
}
|
||||
|
||||
public void validateTable(TableMetadata tmd, CasserMappingEntity<?> entity) {
|
||||
|
||||
if (tmd == null) {
|
||||
throw new CasserException("table not exists " + entity.getTableName() + "for entity " + entity.getMappingInterface());
|
||||
}
|
||||
|
||||
String cql = SchemaUtil.alterTableCql(tmd, entity, dropRemovedColumns);
|
||||
|
||||
if (cql != null) {
|
||||
throw new CasserException("schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + cql);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTable(TableMetadata tmd, CasserMappingEntity<?> entity) {
|
||||
|
||||
if (tmd == null) {
|
||||
createTable(entity);
|
||||
}
|
||||
|
||||
String cql = SchemaUtil.alterTableCql(tmd, entity, dropRemovedColumns);
|
||||
|
||||
if (cql != null) {
|
||||
sessionOps.execute(cql);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
26
src/main/java/casser/core/UserTypeOperations.java
Normal file
26
src/main/java/casser/core/UserTypeOperations.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Noorq, Inc.
|
||||
*
|
||||
* 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 casser.core;
|
||||
|
||||
public final class UserTypeOperations {
|
||||
|
||||
private final AbstractSessionOperations sessionOps;
|
||||
|
||||
public UserTypeOperations(AbstractSessionOperations sessionOps) {
|
||||
this.sessionOps = sessionOps;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,26 +17,53 @@ package casser.mapping;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import casser.support.CasserException;
|
||||
import casser.support.CasserMappingException;
|
||||
|
||||
public class CasserMappingRepository {
|
||||
|
||||
private ConcurrentMap<Class<?>, CasserMappingEntity<?>> entityMap = new ConcurrentHashMap<Class<?>, CasserMappingEntity<?>>();
|
||||
private final Map<Class<?>, CasserMappingEntity<?>> entityMap = new HashMap<Class<?>, CasserMappingEntity<?>>();
|
||||
|
||||
private ConcurrentMap<String, CasserMappingUserType<?>> udtMap = new ConcurrentHashMap<String, CasserMappingUserType<?>>();
|
||||
private final Map<String, CasserMappingUserType<?>> udtMap = new HashMap<String, CasserMappingUserType<?>>();
|
||||
|
||||
public void addEntities(Object[] dsls) {
|
||||
private boolean readOnly = false;
|
||||
|
||||
for (Object dsl : dsls) {
|
||||
public CasserMappingRepository setReadOnly() {
|
||||
this.readOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Class<?> iface = MappingUtil.getMappingInterface(dsl);
|
||||
|
||||
entityMap.putIfAbsent(iface, new CasserMappingEntity(iface));
|
||||
public void addUserType(String name, Class<?> userTypeClass) {
|
||||
|
||||
if (readOnly) {
|
||||
throw new CasserException("read-only mode");
|
||||
}
|
||||
|
||||
udtMap.putIfAbsent(name, new CasserMappingUserType(userTypeClass));
|
||||
|
||||
}
|
||||
|
||||
public Collection<CasserMappingUserType<?>> getKnownUserTypes() {
|
||||
return Collections.unmodifiableCollection(udtMap.values());
|
||||
}
|
||||
|
||||
public CasserMappingUserType<?> findUserType(Class<?> userTypeClass) {
|
||||
return udtMap.get(userTypeClass);
|
||||
}
|
||||
|
||||
public void addEntity(Object dsl) {
|
||||
|
||||
if (readOnly) {
|
||||
throw new CasserException("read-only mode");
|
||||
}
|
||||
|
||||
Class<?> iface = MappingUtil.getMappingInterface(dsl);
|
||||
|
||||
entityMap.putIfAbsent(iface, new CasserMappingEntity(iface));
|
||||
|
||||
}
|
||||
|
||||
public Collection<CasserMappingEntity<?>> getKnownEntities() {
|
||||
|
|
|
@ -28,6 +28,24 @@ public final class MappingUtil {
|
|||
private MappingUtil() {
|
||||
}
|
||||
|
||||
public static String getUserDefinedTypeName(Class<?> clazz) {
|
||||
|
||||
UserDefinedType userDefinedType = clazz.getDeclaredAnnotation(UserDefinedType.class);
|
||||
|
||||
if (userDefinedType != null) {
|
||||
|
||||
String name = userDefinedType.value();
|
||||
|
||||
if (name != null && name.isEmpty()) {
|
||||
name = null;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Class<?> getMappingInterface(Object entity) {
|
||||
|
||||
Class<?> iface = null;
|
||||
|
|
|
@ -38,7 +38,7 @@ public class CompondKeyTest extends AbstractEmbeddedCassandraTest {
|
|||
|
||||
timeline = Casser.dsl(Timeline.class);
|
||||
|
||||
session = Casser.init(getSession()).showCql().createDrop(Timeline.class).get();
|
||||
session = Casser.init(getSession()).showCql().add(Timeline.class).autoCreateDrop().get();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
|
|||
|
||||
user = Casser.dsl(User.class);
|
||||
|
||||
session = Casser.init(getSession()).showCql().createDrop(User.class).get();
|
||||
session = Casser.init(getSession()).showCql().add(User.class).autoCreateDrop().get();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue