blob: bd24d9b3a0436f77d3f92a8e5408ee01b0f78808 [file]
/*
* 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.mapping;
import java.util.*;
import com.datastax.driver.core.*;
import com.datastax.driver.core.exceptions.CodecNotFoundException;
import com.google.common.collect.Maps;
import org.assertj.core.data.MapEntry;
import org.testng.annotations.BeforeMethod;
import com.google.common.base.Objects;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.*;
public class MapperUDTTest extends CCMBridge.PerClassSingleNodeCluster {
protected Collection<String> getTableDefinitions() {
return Arrays.asList("CREATE TYPE address (street text, city text, zip_code int, phones set<text>)",
"CREATE TABLE users (user_id uuid PRIMARY KEY, name text, mainaddress frozen<address>, other_addresses map<text,frozen<address>>)");
}
@BeforeMethod(groups = "short")
public void clean() {
session.execute("TRUNCATE users");
}
@Table(name = "users",
readConsistency = "QUORUM",
writeConsistency = "QUORUM")
public static class User {
@PartitionKey
@Column(name = "user_id")
private UUID userId;
private String name;
@Frozen
private Address mainAddress;
@Column(name = "other_addresses")
@FrozenValue
private Map<String, Address> otherAddresses = Maps.newHashMap();
public User() {
}
public User(String name, Address address) {
this.userId = UUIDs.random();
this.name = name;
this.mainAddress = address;
this.otherAddresses = new HashMap<String, Address>();
}
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getMainAddress() {
return mainAddress;
}
public void setMainAddress(Address address) {
this.mainAddress = address;
}
public Map<String, Address> getOtherAddresses() {
return otherAddresses;
}
public void setOtherAddresses(Map<String, Address> otherAddresses) {
this.otherAddresses = otherAddresses;
}
public void addOtherAddress(String name, Address address) {
this.otherAddresses.put(name, address);
}
@Override
public boolean equals(Object other) {
if (other instanceof User) {
User that = (User) other;
return Objects.equal(this.userId, that.userId) &&
Objects.equal(this.name, that.name) &&
Objects.equal(this.mainAddress, that.mainAddress) &&
Objects.equal(this.otherAddresses, that.otherAddresses);
}
return false;
}
}
@UDT(name = "address")
public static class Address {
// Dummy constant to test that static fields are properly ignored
public static final int FOO = 1;
private String street;
@Field // not strictly required, but we want to check that the annotation works without a name
private String city;
@Field(name = "zip_code")
private int zipCode;
private Set<String> phones;
public Address() {
}
public Address(String street, String city, int zipCode, String... phones) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
this.phones = new HashSet<String>();
for (String phone : phones) {
this.phones.add(phone);
}
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipCode() {
return zipCode;
}
public void setZipCode(int zipCode) {
this.zipCode = zipCode;
}
public Set<String> getPhones() {
return phones;
}
public void setPhones(Set<String> phones) {
this.phones = phones;
}
@Override
public boolean equals(Object other) {
if (other instanceof Address) {
Address that = (Address) other;
return Objects.equal(this.street, that.street) &&
Objects.equal(this.city, that.city) &&
Objects.equal(this.zipCode, that.zipCode) &&
Objects.equal(this.phones, that.phones);
}
return false;
}
}
@Accessor
public interface UserAccessor {
@Query("SELECT * FROM users WHERE user_id=:userId")
User getOne(@Param("userId") UUID userId);
@Query("UPDATE users SET other_addresses[:name]=:address WHERE user_id=:id")
ResultSet addAddress(@Param("id") UUID id, @Param("name") String addressName, @Param("address") Address address);
@Query("UPDATE users SET other_addresses=:addresses where user_id=:id")
ResultSet setOtherAddresses(@Param("id") UUID id, @Param("addresses") Map<String, Address> addresses);
@Query("SELECT * FROM users")
public Result<User> getAll();
}
@Test(groups = "short")
public void testSimpleEntity() throws Exception {
Mapper<User> m = new MappingManager(session).mapper(User.class);
User u1 = new User("Paul", new Address("12 4th Street", "Springfield", 12345, "12341343", "435423245"));
u1.addOtherAddress("work", new Address("5 Main Street", "Springfield", 12345, "23431342"));
m.save(u1);
assertEquals(m.get(u1.getUserId()), u1);
}
@Test(groups = "short")
public void should_handle_null_UDT_value() throws Exception {
Mapper<User> m = new MappingManager(session).mapper(User.class);
User u1 = new User("Paul", null);
m.save(u1);
assertNull(m.get(u1.getUserId()).getMainAddress());
}
@Test(groups = "short")
public void testAccessor() throws Exception {
MappingManager manager = new MappingManager(session);
Mapper<User> m = new MappingManager(session).mapper(User.class);
User u1 = new User("Paul", new Address("12 4th Street", "Springfield", 12345, "12341343", "435423245"));
m.save(u1);
UserAccessor userAccessor = manager.createAccessor(UserAccessor.class);
Address workAddress = new Address("5 Main Street", "Springfield", 12345, "23431342");
userAccessor.addAddress(u1.getUserId(), "work", workAddress);
User u2 = userAccessor.getOne(u1.getUserId());
assertEquals(workAddress, u2.getOtherAddresses().get("work"));
// Adding a null value should remove it from the list.
userAccessor.addAddress(u1.getUserId(), "work", null);
User u3 = userAccessor.getOne(u1.getUserId());
assertThat(u3.getOtherAddresses()).doesNotContainKey("work");
// Add a bunch of other addresses
Map<String, Address> otherAddresses = Maps.newHashMap();
otherAddresses.put("work", workAddress);
otherAddresses.put("cabin", new Address("42 Middle of Nowhere", "Lake of the Woods", 49553, "8675309"));
userAccessor.setOtherAddresses(u1.getUserId(), otherAddresses);
User u4 = userAccessor.getOne(u1.getUserId());
assertThat(u4.getOtherAddresses()).isEqualTo(otherAddresses);
// Nullify other addresses
userAccessor.setOtherAddresses(u1.getUserId(), null);
User u5 = userAccessor.getOne(u1.getUserId());
assertThat(u5.getOtherAddresses()).isEmpty();
// No argument call
Result<User> u = userAccessor.getAll();
assertEquals(u.one(), u5);
assertTrue(u.isExhausted());
}
@Test(groups = "short")
public void should_be_able_to_use_udtCodec_standalone() {
// Create a separate Cluster/Session to start with a CodecRegistry from scratch (so not already registered).
Cluster cluster = Cluster.builder().addContactPointsWithPorts(Collections.singleton(hostAddress)).build();
try {
CodecRegistry registry = cluster.getConfiguration().getCodecRegistry();
Session session = cluster.connect(keyspace);
UUID userId = UUIDs.random();
// Create a user.
session.execute("update users SET other_addresses['condo']={street: '101 Ocean Ln', city: 'Jacksonville, FL', zip_code: 89898, phones: {'8675309'}} " +
" WHERE user_id=" + TypeCodec.UUIDCodec.instance.format(userId));
session.execute("update users SET mainaddress={street: '42 Middle of Nowhere', city: 'Lake of the Woods', zip_code: 49553, phones: {'8675039'}} " +
" WHERE user_Id=" + TypeCodec.UUIDCodec.instance.format(userId));
// Get the user.
Row row = session.execute("select * from users where user_id=?", userId).one();
UDTValue udtValue = row.getUDTValue("mainaddress");
assertThat(udtValue.getString("street")).isEqualTo("42 Middle of Nowhere");
Object udtObject = row.getObject("mainaddress");
assertThat(udtObject).isEqualTo(udtValue);
// There shouldn't be a codec for address.
try {
assertThat(registry.codecFor(udtValue.getType(), Address.class));
fail("Didn't expect to find codec for udtType <-> Address");
} catch (CodecNotFoundException e) {
// expected.
}
// Expect a this_udt <-> UDTValue codec to exist. This is a pretty safe bet or else we wouldn't get
// this value back.
TypeCodec<UDTValue> udtCodec = registry.codecFor(udtValue.getType());
assertThat(udtCodec.getCqlType()).isEqualTo(udtValue.getType());
assertThat(udtCodec.getJavaType().getRawType()).isEqualTo(UDTValue.class);
// Retrieve codec for Address, if it can be mapped it will be created, if already registered it'll be used.
TypeCodec<Address> codec = new MappingManager(session).udtCodec(Address.class);
// The codec should be registered after we call udtCodec.
assertThat(registry.codecFor(udtValue.getType(), Address.class)).isEqualTo(codec);
// Should be able to retrieve as an Address.
Address mainAddress = row.get("mainaddress", Address.class);
assertThat(mainAddress.getCity()).isEqualTo("Lake of the Woods");
assertThat(mainAddress.getStreet()).isEqualTo("42 Middle of Nowhere");
assertThat(mainAddress.getZipCode()).isEqualTo(49553);
assertThat(mainAddress.getPhones()).containsExactly("8675039");
// Should be able to retrieve within a Map.
Address expectedOtherAddress = new Address();
expectedOtherAddress.setStreet("101 Ocean Ln");
expectedOtherAddress.setCity("Jacksonville, FL");
expectedOtherAddress.setZipCode(89898);
expectedOtherAddress.setPhones(Collections.singleton("8675309"));
Map<String, Address> otherAddresses = row.getMap("other_addresses", String.class, Address.class);
assertThat(otherAddresses).containsOnly(MapEntry.entry("condo", expectedOtherAddress));
} finally {
cluster.close();
}
}
}