/*
* 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.core;
import com.datastax.driver.core.utils.CassandraVersion;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.testng.annotations.Test;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@CassandraVersion("2.1.0")
public class TypeCodecUDTIntegrationTest extends CCMTestsSupport {
private final String insertQuery = "INSERT INTO users (id, name, address) VALUES (?, ?, ?)";
private final String selectQuery = "SELECT id, name, address FROM users WHERE id = ?";
private final UUID uuid = UUID.randomUUID();
private final Phone phone1 = new Phone("1234567", Sets.newHashSet("home", "iphone"));
private final Phone phone2 = new Phone("2345678", Sets.newHashSet("work"));
private final Address address = new Address("blah", 75010, Lists.newArrayList(phone1, phone2));
private UserType addressType;
private UserType phoneType;
private UDTValue addressValue;
@Override
public void onTestContextInitialized() {
execute(
"CREATE TYPE IF NOT EXISTS \"phone\" (number text, tags set<text>)",
"CREATE TYPE IF NOT EXISTS \"address\" (street text, zipcode int, phones list<frozen<phone>>)",
"CREATE TABLE IF NOT EXISTS \"users\" (id uuid PRIMARY KEY, name text, address frozen<address>)"
);
}
@Test(groups = "short")
public void should_handle_udts_with_default_codecs() {
setUpUserTypes(cluster());
// simple statement
session().execute(insertQuery, uuid, "John Doe", addressValue);
ResultSet rows = session().execute(selectQuery, uuid);
Row row = rows.one();
assertRow(row);
// prepared + values
PreparedStatement ps = session().prepare(insertQuery);
session().execute(ps.bind(uuid, "John Doe", addressValue));
rows = session().execute(selectQuery, uuid);
row = rows.one();
assertRow(row);
// bound with setUDTValue
session().execute(ps.bind().setUUID(0, uuid).setString(1, "John Doe").setUDTValue(2, addressValue));
rows = session().execute(selectQuery, uuid);
row = rows.one();
assertRow(row);
}
@Test(groups = "short")
public void should_handle_udts_with_custom_codecs() {
CodecRegistry codecRegistry = new CodecRegistry();
Cluster cluster = register(Cluster.builder()
.addContactPoints(getContactPoints())
.withPort(ccm().getBinaryPort())
.withCodecRegistry(codecRegistry)
.build());
Session session = cluster.connect(keyspace);
setUpUserTypes(cluster);
TypeCodec<UDTValue> addressTypeCodec = TypeCodec.userType(addressType);
TypeCodec<UDTValue> phoneTypeCodec = TypeCodec.userType(phoneType);
codecRegistry
.register(new AddressCodec(addressTypeCodec, Address.class))
.register(new PhoneCodec(phoneTypeCodec, Phone.class))
;
session.execute(insertQuery, uuid, "John Doe", address);
ResultSet rows = session.execute(selectQuery, uuid);
Row row = rows.one();
assertThat(row.getUUID(0)).isEqualTo(uuid);
assertThat(row.getObject(0)).isEqualTo(uuid);
assertThat(row.get(0, UUID.class)).isEqualTo(uuid);
assertThat(row.getString(1)).isEqualTo("John Doe");
assertThat(row.getObject(1)).isEqualTo("John Doe");
assertThat(row.get(1, String.class)).isEqualTo("John Doe");
assertThat(row.getUDTValue(2)).isEqualTo(addressValue);
// corner case: getObject should use default codecs;
// but tuple and udt codecs are registered on the fly;
// so if we have another manually-registered codec
// that one will be picked up :(
assertThat(row.getObject(2)).isEqualTo(address);
assertThat(row.get(2, UDTValue.class)).isEqualTo(addressValue);
assertThat(row.get(2, Address.class)).isEqualTo(address);
}
private void assertRow(Row row) {
assertThat(row.getUUID(0)).isEqualTo(uuid);
assertThat(row.getObject(0)).isEqualTo(uuid);
assertThat(row.get(0, UUID.class)).isEqualTo(uuid);
assertThat(row.getString(1)).isEqualTo("John Doe");
assertThat(row.getObject(1)).isEqualTo("John Doe");
assertThat(row.get(1, String.class)).isEqualTo("John Doe");
assertThat(row.getUDTValue(2)).isEqualTo(addressValue);
assertThat(row.getObject(2)).isEqualTo(addressValue);
assertThat(row.get(2, UDTValue.class)).isEqualTo(addressValue);
}
private void setUpUserTypes(Cluster cluster) {
addressType = cluster.getMetadata().getKeyspace(keyspace).getUserType("address");
phoneType = cluster.getMetadata().getKeyspace(keyspace).getUserType("phone");
UDTValue phone1Value = phoneType.newValue()
.setString("number", phone1.number)
.setSet("tags", phone1.tags);
UDTValue phone2Value = phoneType.newValue()
.setString("number", phone2.number)
.setSet("tags", phone2.tags);
addressValue = addressType.newValue()
.setString("street", address.street)
.setInt(1, address.zipcode)
.setList("phones", Lists.newArrayList(phone1Value, phone2Value));
}
static class AddressCodec extends MappingCodec<Address, UDTValue> {
private final UserType userType;
public AddressCodec(TypeCodec<UDTValue> innerCodec, Class<Address> javaType) {
super(innerCodec, javaType);
userType = (UserType) innerCodec.getCqlType();
}
@Override
protected Address deserialize(UDTValue value) {
return value == null ? null : new Address(value.getString("street"), value.getInt("zipcode"), value.getList("phones", Phone.class));
}
@Override
protected UDTValue serialize(Address value) {
return value == null ? null : userType.newValue().setString("street", value.street).setInt("zipcode", value.zipcode).setList("phones", value.phones, Phone.class);
}
}
static class PhoneCodec extends MappingCodec<Phone, UDTValue> {
private final UserType userType;
public PhoneCodec(TypeCodec<UDTValue> innerCodec, Class<Phone> javaType) {
super(innerCodec, javaType);
userType = (UserType) innerCodec.getCqlType();
}
@Override
protected Phone deserialize(UDTValue value) {
return value == null ? null : new Phone(value.getString("number"), value.getSet("tags", String.class));
}
@Override
protected UDTValue serialize(Phone value) {
return value == null ? null : userType.newValue().setString("number", value.number).setSet("tags", value.tags);
}
}
static class Address {
String street;
int zipcode;
List<Phone> phones;
public Address(String street, int zipcode, List<Phone> phones) {
this.street = street;
this.zipcode = zipcode;
this.phones = phones;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Address address = (Address) o;
return zipcode == address.zipcode && street.equals(address.street) && phones.equals(address.phones);
}
@Override
public int hashCode() {
int result = street.hashCode();
result = 31 * result + zipcode;
result = 31 * result + phones.hashCode();
return result;
}
}
static class Phone {
String number;
Set<String> tags;
public Phone(String number, Set<String> tags) {
this.number = number;
this.tags = tags;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Phone phone = (Phone) o;
return number.equals(phone.number) && tags.equals(phone.tags);
}
@Override
public int hashCode() {
int result = number.hashCode();
result = 31 * result + tags.hashCode();
return result;
}
}
}