/** * Update the married status of a new user record. * * <p>This method demonstrates updating both a UserProfileModel and a UserActionsModel with a * single HBase request using the composite dao. It performs a get/update/put operation, which is * protected by the check_conflict field on UserProfileModel from colliding with another * get/update/put operation. * * @param firstName The first name of the user we are updating * @param lastName The last name of the user we are updating * @param married True if this person is married. Otherwise false. */ public void updateUserProfile(String firstName, String lastName, boolean married) { // Get the timestamp we'll use to set the value of the profile_updated // action. long ts = System.currentTimeMillis(); // Construct the key we'll use to fetch the user. PartitionKey key = userProfileActionsDao.getPartitionStrategy().partitionKey(lastName, firstName); // Get the profile and actions entity from the composite dao. UserProfileActionsModel profileActionsModel = userProfileActionsDao.get(key); // Updating the married status is hairy since our avro compiler isn't setup // to compile setters for fields. We have to construct a clone through the // builder. UserProfileActionsModel updatedProfileActionsModel = UserProfileActionsModel.newBuilder(profileActionsModel) .setUserProfileModel( UserProfileModel.newBuilder(profileActionsModel.getUserProfileModel()) .setMarried(married) .build()) .build(); // Since maps are mutable, we can update the actions map without having to // go through the builder like above. updatedProfileActionsModel .getUserActionsModel() .getActions() .put("profile_updated", Long.toString(ts)); if (!userProfileActionsDao.put(updatedProfileActionsModel)) { // If put returns false, a write conflict occurred where someone else // updated the row between the times we did the get and put. System.out.println("Updating the user profile failed due to a write conflict"); } }
/** * Print the user profiles and actions for all users with the provided last name * * <p>This method demonstrates how to open a scanner with a start key. It's using the composite * dao, so the records it returns will be a composite of both the profile model and actions model. * * @param lastName The last name of users to scan. */ public void printUserProfileActionsForLastName(String lastName) { // Create a partial key that will allow us to start the scanner from the // first user record that has last name equal to the one provided. PartitionKey startKey = userProfileActionsDao.getPartitionStrategy().partitionKey("lastName"); // Get the scanner with the start key. Null for stopKey in the getScanner // method indicates that the scanner will scan to the end of the table. Our // loop will break out when it encounters a record without the last name. EntityScanner<UserProfileActionsModel> scanner = userProfileActionsDao.getScanner(startKey, null); scanner.open(); try { // scan until we find a last name not equal to the one provided for (UserProfileActionsModel entity : scanner) { if (!entity.getUserProfileModel().getLastName().equals(lastName)) { // last name of row different, break out of the scan. break; } System.out.println(entity.toString()); } } finally { // scanners need to be closed. scanner.close(); } }
@Test public void testSpecific() throws Exception { // Construct Dao Dao<CompositeRecord> dao = SpecificAvroDao.buildCompositeDao( tablePool, tableName, Arrays.asList(subRecord1String, subRecord2String), CompositeRecord.class); // Construct records SubRecord1 subRecord1 = SubRecord1.newBuilder() .setKeyPart1("1") .setKeyPart2("1") .setField1("field1_1") .setField2("field1_2") .build(); SubRecord2 subRecord2 = SubRecord2.newBuilder() .setKeyPart1("1") .setKeyPart2("1") .setField1("field2_1") .setField2("field2_2") .build(); CompositeRecord compositeRecord = CompositeRecord.newBuilder().setSubRecord1(subRecord1).setSubRecord2(subRecord2).build(); // Test put and get dao.put(compositeRecord); PartitionKey key = dao.getPartitionStrategy().partitionKey("1", "1"); CompositeRecord returnedCompositeRecord = dao.get(key); assertEquals("field1_1", returnedCompositeRecord.getSubRecord1().getField1()); assertEquals("field1_2", returnedCompositeRecord.getSubRecord1().getField2()); assertEquals("field2_1", returnedCompositeRecord.getSubRecord2().getField1()); assertEquals("field2_2", returnedCompositeRecord.getSubRecord2().getField2()); // Test OCC assertFalse(dao.put(compositeRecord)); assertTrue(dao.put(returnedCompositeRecord)); // Test null field subRecord1 = SubRecord1.newBuilder() .setKeyPart1("1") .setKeyPart2("2") .setField1("field1_1") .setField2("field1_2") .build(); compositeRecord = CompositeRecord.newBuilder().setSubRecord1(subRecord1).build(); dao.put(compositeRecord); key = dao.getPartitionStrategy().partitionKey("1", "2"); compositeRecord = dao.get(key); assertEquals(null, compositeRecord.getSubRecord2()); }
/** * Create a fresh new user record. * * <p>This method demonstrates creating both a UserProfileModel and a UserActionsModel atomically * in a single row. When creating a user profile, we add the "created" action to the actions map. * This shows how we can use the CompositeDao to accomplish this. * * @param firstName The first name of the user we are creating * @param lastName The last name of the user we are creating * @param married True if this person is married. Otherwise false. */ public void create(String firstName, String lastName, boolean married) { long ts = System.currentTimeMillis(); UserProfileModel profileModel = UserProfileModel.newBuilder() .setFirstName(firstName) .setLastName(lastName) .setMarried(married) .setCreated(ts) .build(); UserActionsModel actionsModel = UserActionsModel.newBuilder() .setFirstName(firstName) .setLastName(lastName) .setActions(new HashMap<String, String>()) .build(); actionsModel.getActions().put("profile_created", Long.toString(ts)); UserProfileActionsModel profileActionsModel = UserProfileActionsModel.newBuilder() .setUserProfileModel(profileModel) .setUserActionsModel(actionsModel) .build(); if (!userProfileActionsDao.put(profileActionsModel)) { // If put returns false, a user already existed at this row System.out.println("Creating a new user profile failed due to a write conflict."); } }
/** * Print all user profiles. * * <p>This method demonstrates how to open a scanner that will scan the entire table. It has no * start or stop keys specified. */ public void printUserProfies() { EntityScanner<UserProfileModel> scanner = userProfileDao.getScanner(); scanner.open(); try { for (UserProfileModel entity : scanner) { System.out.println(entity.toString()); } } finally { // scanners need to be closed. scanner.close(); } }
/** * Add an action to the user profile. * * <p>This method demonstrates how we can use a keyAsColumn map field (the actions field of the * UserActionsModel) to add values to the map without having to do a get/update/put operation. * When doing the put, it won't remove columns that exist in the row that aren't in the new map we * are putting. It will just add the additional columns we are now putting to the row. * * @param firstName The first name of the user we are updating * @param lastName The last name of the user we are updating * @param actionType A string representing the action type which is the key of the map * @param actionValue A string representing the action value. */ public void addAction(String firstName, String lastName, String actionType, String actionValue) { // Create a new UserActionsModel, and add a new actions map to it with a // single action value. Even if one exists in this row, since it has a lone // keyAsColumn field, it won't remove any actions that already exist in the // actions column family. UserActionsModel actionsModel = UserActionsModel.newBuilder() .setLastName(lastName) .setFirstName(firstName) .setActions(new HashMap<String, String>()) .build(); actionsModel.getActions().put(actionType, actionValue); // Perform the put. userActionsDao.put(actionsModel); }