/* * Copyright (C) 2012-2015 DataStax 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 com.datastax.driver.extras.codecs.enums; import com.datastax.driver.core.*; import com.datastax.driver.core.utils.CassandraVersion; import com.datastax.driver.mapping.Mapper; import com.datastax.driver.mapping.MappingManager; import com.datastax.driver.mapping.annotations.*; import com.google.common.collect.ImmutableMap; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.Map; import java.util.Set; import static com.datastax.driver.core.DataType.cint; import static com.datastax.driver.core.DataType.text; import static com.datastax.driver.extras.codecs.enums.EnumCodecsTest.Bar.BAR_1; import static com.datastax.driver.extras.codecs.enums.EnumCodecsTest.Bar.BAR_2; import static com.datastax.driver.extras.codecs.enums.EnumCodecsTest.Foo.FOO_1; import static com.datastax.driver.extras.codecs.enums.EnumCodecsTest.Foo.FOO_2; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Sets.newHashSet; import static org.assertj.core.api.Assertions.assertThat; /** * A test that validates that Enums are correctly mapped to varchars (with EnumNameCodec) * and ints (with EnumOrdinalCodec). * It also validates that both codecs may coexist in the same CodecRegistry. */ @CassandraVersion("2.1.0") public class EnumCodecsTest extends CCMTestsSupport { private final String insertQuery = "INSERT INTO t1 (pk, foo, foos, bar, bars, foobars, tup, udt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; private final String selectQuery = "SELECT pk, foo, foos, bar, bars, foobars, tup, udt FROM t1 WHERE pk = ?"; private final int pk = 42; private final List<Foo> foos = newArrayList(FOO_1, FOO_2); private final Set<Bar> bars = newHashSet(BAR_1, BAR_2); private final Map<Foo, Bar> foobars = ImmutableMap.of(FOO_1, BAR_1, FOO_2, BAR_2); private TupleValue tupleValue; private UDTValue udtValue; @Override public void onTestContextInitialized() { execute( "CREATE TYPE IF NOT EXISTS udt1 (" + "foo int," + "bar text)", "CREATE TABLE IF NOT EXISTS t1 (" + "pk int, " + "foo int, " + "foos list<int>, " + "bar text, " + "bars set<text>, " + "foobars map<int,text>, " + "tup frozen<tuple<int,varchar>>, " + "udt frozen<udt1>," + "primary key (pk, foo))" ); } @Override public Cluster.Builder createClusterBuilder() { return Cluster.builder().withCodecRegistry( new CodecRegistry() .register(new EnumOrdinalCodec<Foo>(Foo.class)) .register(new EnumNameCodec<Bar>(Bar.class)) ); } @BeforeMethod(groups = "short") public void before() { TupleType tup = cluster().getMetadata().newTupleType(cint(), text()); tupleValue = tup.newValue() .set(0, FOO_1, Foo.class) .set(1, BAR_1, Bar.class); UserType udt = cluster().getMetadata().getKeyspace(keyspace).getUserType("udt1"); udtValue = udt.newValue() .set("foo", FOO_1, Foo.class) .set("bar", BAR_1, Bar.class); } @Test(groups = "short") public void should_use_enum_codecs_with_simple_statements() { session().execute(insertQuery, pk, FOO_1, foos, BAR_1, bars, foobars, tupleValue, udtValue); ResultSet rows = session().execute(selectQuery, pk); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_enum_codecs_with_prepared_statements_1() { session().execute(session().prepare(insertQuery).bind(pk, FOO_1, foos, BAR_1, bars, foobars, tupleValue, udtValue)); PreparedStatement ps = session().prepare(selectQuery); ResultSet rows = session().execute(ps.bind(pk)); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_enum_codecs_with_prepared_statements_2() { session().execute(session().prepare(insertQuery).bind() .setInt(0, pk) .set(1, FOO_1, Foo.class) .setList(2, foos, Foo.class) .set(3, BAR_1, Bar.class) .set(4, bars, TypeTokens.setOf(Bar.class)) .setMap(5, foobars, Foo.class, Bar.class) .setTupleValue(6, tupleValue) .setUDTValue(7, udtValue) ); PreparedStatement ps = session().prepare(selectQuery); ResultSet rows = session().execute(ps.bind() .setInt(0, pk) ); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_mapper_to_store_and_retrieve_nulls_with_enum_codecs() { // given MappingManager manager = new MappingManager(session()); Mapper<Mapped> mapper = manager.mapper(Mapped.class); Mapped pojo = new Mapped(); pojo.pk = 42; pojo.foo = FOO_1; // when mapper.save(pojo); Mapped actual = mapper.get(42, FOO_1); // then assertThat(actual).isEqualToComparingFieldByField(pojo); } @Test(groups = "short") public void should_use_mapper_to_store_and_retrieve_values_with_enum_codecs() { // given MappingManager manager = new MappingManager(session()); Mapper<Mapped> mapper = manager.mapper(Mapped.class); Mapped pojo = new Mapped(); pojo.pk = 42; pojo.foo = FOO_1; pojo.bar = BAR_1; pojo.foos = newArrayList(FOO_2, FOO_1); pojo.bars = newHashSet(BAR_1, BAR_2); pojo.foobars = ImmutableMap.of(FOO_1, BAR_2); // when mapper.save(pojo); Mapped actual = mapper.get(42, FOO_1); // then assertThat(actual).isEqualToComparingFieldByField(pojo); } @Test(groups = "short") public void should_use_accessor_to_store_and_retrieve_values_with_enum_codecs() { // given MappedAccessor accessor = new MappingManager(session()).createAccessor(MappedAccessor.class); Mapped expected = new Mapped(); expected.pk = 42; expected.foo = FOO_1; expected.bar = BAR_2; // when accessor.insert(42, FOO_1, BAR_2); Mapped result = accessor.getByFoo(42, FOO_1); // then assertThat(result).isEqualToComparingFieldByField(expected); } private void assertRow(Row row) { assertThat(row.getInt(0)).isEqualTo(pk); assertThat(row.getObject(1)).isEqualTo(FOO_1.ordinal()); // uses the built-in IntCodec because CQL type is int assertThat(row.getInt("foo")).isEqualTo(FOO_1.ordinal()); // uses the built-in IntCodec assertThat(row.get(1, Integer.class)).isEqualTo(FOO_1.ordinal()); // forces IntCodec assertThat(row.get("foo", Foo.class)).isEqualTo(FOO_1); // forces EnumOrdinalCodec assertThat(row.getObject(2)).isEqualTo(newArrayList(FOO_1.ordinal(), FOO_2.ordinal())); // uses the built-in ListCodec(IntCodec) because CQL type is list<int> assertThat(row.getList(2, Integer.class)).isEqualTo(newArrayList(FOO_1.ordinal(), FOO_2.ordinal())); assertThat(row.getList("foos", Foo.class)).isEqualTo(newArrayList(FOO_1, FOO_2)); assertThat(row.get(2, TypeTokens.listOf(Integer.class))).isEqualTo(newArrayList(FOO_1.ordinal(), FOO_2.ordinal())); assertThat(row.get("foos", TypeTokens.listOf(Foo.class))).isEqualTo(newArrayList(FOO_1, FOO_2)); assertThat(row.getObject(3)).isEqualTo(BAR_1.name()); // uses the built-in VarcharCodec because CQL type is varchar assertThat(row.getString("bar")).isEqualTo(BAR_1.name()); // forces VarcharCodec assertThat(row.get(3, String.class)).isEqualTo(BAR_1.name()); // forces VarcharCodec assertThat(row.get("bar", Bar.class)).isEqualTo(BAR_1); // forces EnumNameCodec assertThat(row.getObject(4)).isEqualTo(newHashSet(BAR_1.name(), BAR_2.name())); // uses the built-in SetCodec(VarcharCodec) because CQL type is set<varchar> assertThat(row.getSet(4, String.class)).isEqualTo(newHashSet(BAR_1.name(), BAR_2.name())); assertThat(row.getSet("bars", Bar.class)).isEqualTo(newHashSet(BAR_1, BAR_2)); assertThat(row.get(4, TypeTokens.setOf(String.class))).isEqualTo(newHashSet(BAR_1.name(), BAR_2.name())); assertThat(row.get("bars", TypeTokens.setOf(Bar.class))).isEqualTo(newHashSet(BAR_1, BAR_2)); // uses default built-in codec MapCodec(IntCodec, VarcharCodec) because CQL type is map<int, varchar> assertThat(row.getObject(5)).isEqualTo(ImmutableMap.of(FOO_1.ordinal(), BAR_1.name(), FOO_2.ordinal(), BAR_2.name())); // getMap with combinations of built-in or EnumX codecs assertThat(row.getMap(5, Integer.class, String.class)).isEqualTo(ImmutableMap.of(FOO_1.ordinal(), BAR_1.name(), FOO_2.ordinal(), BAR_2.name())); assertThat(row.getMap(5, Foo.class, String.class)).isEqualTo(ImmutableMap.of(FOO_1, BAR_1.name(), FOO_2, BAR_2.name())); assertThat(row.getMap(5, Integer.class, Bar.class)).isEqualTo(ImmutableMap.of(FOO_1.ordinal(), BAR_1, FOO_2.ordinal(), BAR_2)); assertThat(row.getMap(5, Foo.class, Bar.class)).isEqualTo(ImmutableMap.of(FOO_1, BAR_1, FOO_2, BAR_2)); // get + TypeToken with combinations of built-in or EnumX codecs assertThat(row.get("foobars", TypeTokens.mapOf(Integer.class, String.class))).isEqualTo(ImmutableMap.of(FOO_1.ordinal(), BAR_1.name(), FOO_2.ordinal(), BAR_2.name())); assertThat(row.get("foobars", TypeTokens.mapOf(Foo.class, String.class))).isEqualTo(ImmutableMap.of(FOO_1, BAR_1.name(), FOO_2, BAR_2.name())); assertThat(row.get("foobars", TypeTokens.mapOf(Integer.class, Bar.class))).isEqualTo(ImmutableMap.of(FOO_1.ordinal(), BAR_1, FOO_2.ordinal(), BAR_2)); assertThat(row.get("foobars", TypeTokens.mapOf(Foo.class, Bar.class))).isEqualTo(ImmutableMap.of(FOO_1, BAR_1, FOO_2, BAR_2)); assertThat(row.getTupleValue(6).getInt(0)).isEqualTo(FOO_1.ordinal()); assertThat(row.get(6, TupleValue.class).get(0, Foo.class)).isEqualTo(FOO_1); assertThat(row.getTupleValue("tup").getString(1)).isEqualTo(BAR_1.name()); assertThat(row.get("tup", TupleValue.class).get(1, Bar.class)).isEqualTo(BAR_1); assertThat(row.getUDTValue(7).getInt("foo")).isEqualTo(FOO_1.ordinal()); assertThat(row.get(7, UDTValue.class).get("foo", Foo.class)).isEqualTo(FOO_1); assertThat(row.getUDTValue("udt").getString("bar")).isEqualTo(BAR_1.name()); assertThat(row.get("udt", UDTValue.class).get("bar", Bar.class)).isEqualTo(BAR_1); } @SuppressWarnings("unused") @Table(name = "t1") public static class Mapped { @PartitionKey private int pk; @ClusteringColumn private Foo foo; private List<Foo> foos; private Bar bar; private Set<Bar> bars; private Map<Foo, Bar> foobars; public int getPk() { return pk; } public void setPk(int pk) { this.pk = pk; } public Foo getFoo() { return foo; } public void setFoo(Foo foo) { this.foo = foo; } public List<Foo> getFoos() { return foos; } public void setFoos(List<Foo> foos) { this.foos = foos; } public Bar getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } public Set<Bar> getBars() { return bars; } public void setBars(Set<Bar> bars) { this.bars = bars; } public Map<Foo, Bar> getFoobars() { return foobars; } public void setFoobars(Map<Foo, Bar> foobars) { this.foobars = foobars; } } @Accessor public interface MappedAccessor { @Query("select * from t1 where pk=? and foo=?") Mapped getByFoo(int pk, Foo foo); @Query("insert into t1 (pk, foo, bar) values (?,?,?)") ResultSet insert(int pk, Foo foo, Bar bar); } enum Foo { FOO_1, FOO_2 } enum Bar { BAR_1, BAR_2 } }