Learn how to write data to DynamoDB and understand the fundamentals of HTTP for building RESTful services.
The DynamoDB Enhanced Client provides several operations for creating and updating items in DynamoDB tables:
To add a new item to a DynamoDB table:
// Create a new MusicItem instance
MusicItem newSong = new MusicItem();
newSong.setArtist("Imagine Dragons");
newSong.setSongTitle("Radioactive");
newSong.setAlbumTitle("Night Visions");
newSong.setYearReleased(2012);
newSong.setGenres(new HashSet<>(Arrays.asList("Alternative Rock", "Indie Rock")));
// Save the item to DynamoDB
musicTable.putItem(newSong);
System.out.println("Added new song: " + newSong.getSongTitle() + " by " + newSong.getArtist());
The putItem
method will overwrite any existing item with the same key, so be careful when using it on items that might already exist.
There are two main approaches to updating items:
putItem
to replace the entire itemupdateItem
to modify specific attributesFor partial updates, you can use the UpdateItemEnhancedRequest
:
// Create a key for the item to update
Key key = Key.builder()
.partitionValue("Imagine Dragons")
.sortValue("Radioactive")
.build();
// Define the update expression
UpdateItemEnhancedRequest<MusicItem> updateRequest = UpdateItemEnhancedRequest
.builder(MusicItem.class)
.key(key)
.updateExpression(Expression.builder()
.expression("SET yearReleased = :year, genres = :genres")
.expressionValues(singletonMap(":year", AttributeValue.builder().n("2013").build()))
.expressionValues(singletonMap(":genres", AttributeValue.builder()
.ss(Arrays.asList("Alternative Rock", "Electronic Rock"))
.build()))
.build())
.build();
// Execute the update
musicTable.updateItem(updateRequest);
DynamoDB supports conditional writes to ensure that operations only proceed if certain conditions are met:
// Create a new song, but only if it doesn't already exist
PutItemEnhancedRequest<MusicItem> putRequest = PutItemEnhancedRequest
.builder(MusicItem.class)
.item(newSong)
.conditionExpression(Expression.builder()
.expression("attribute_not_exists(artist) AND attribute_not_exists(songTitle)")
.build())
.build();
try {
musicTable.putItem(putRequest);
System.out.println("Item added successfully");
} catch (ConditionalCheckFailedException e) {
System.out.println("Song already exists. Cannot overwrite.");
}
For numeric attributes that need to be incremented or decremented:
// Increment the play count for a song
UpdateItemEnhancedRequest<MusicItem> updateCounter = UpdateItemEnhancedRequest
.builder(MusicItem.class)
.key(key)
.updateExpression(Expression.builder()
.expression("SET playCount = if_not_exists(playCount, :zero) + :increment")
.expressionValues(Map.of(
":zero", AttributeValue.builder().n("0").build(),
":increment", AttributeValue.builder().n("1").build()))
.build())
.build();
musicTable.updateItem(updateCounter);
These write operations form the basis of CRUD functionality with DynamoDB, enabling your applications to create and modify data as needed.
HTTP (Hypertext Transfer Protocol) is the foundation of data communication on the web, and RESTful APIs are built on top of HTTP to provide standardized interfaces for systems to communicate.
A URL consists of several components:
https://api.example.com:443/resources/123?filter=active
| | | | |
| | | | └── Query parameters
| | | └── Resource path/endpoint
| | └── Port
| └── Domain name (FQDN)
└── Protocol
REST (Representational State Transfer) is an architectural style for designing networked applications. RESTful APIs use HTTP methods explicitly and follow a stateless client-server model.
A RESTful API is an interface that allows different systems to communicate using HTTP methods to perform operations on resources. Key characteristics include:
There's a natural mapping between HTTP methods and database CRUD operations:
HTTP Method | Database Operation | Description |
---|---|---|
GET | Read | Retrieve resources |
POST | Create | Create new resources |
PUT | Update | Update existing resources |
DELETE | Delete | Remove resources |
This mapping helps design intuitive APIs that follow web standards and are easy to understand for developers.
Building web services often involves connecting RESTful APIs with database operations. Here's how you might structure a Java application that uses both DynamoDB and HTTP:
A typical web service architecture includes:
The flow of data typically follows this pattern:
HTTP Request → API Layer → Service Layer → Data Access Layer → DynamoDB
HTTP Response ← API Layer ← Service Layer ← Data Access Layer ← DynamoDB
Here's a conceptual example of how a music library API might be structured:
// API Layer (Controller)
@RestController
@RequestMapping("/api/songs")
public class SongController {
private final SongService songService;
@Autowired
public SongController(SongService songService) {
this.songService = songService;
}
@GetMapping("/{artist}/{title}")
public ResponseEntity<SongResponse> getSong(@PathVariable String artist,
@PathVariable String title) {
MusicItem song = songService.getSong(artist, title);
return ResponseEntity.ok(convertToResponse(song));
}
@PostMapping
public ResponseEntity<SongResponse> createSong(@RequestBody SongRequest request) {
MusicItem song = songService.createSong(convertToModel(request));
return ResponseEntity.status(HttpStatus.CREATED).body(convertToResponse(song));
}
// Other endpoints: PUT, DELETE, etc.
}
// Service Layer
@Service
public class SongService {
private final SongRepository songRepository;
@Autowired
public SongService(SongRepository songRepository) {
this.songRepository = songRepository;
}
public MusicItem getSong(String artist, String title) {
return songRepository.findByArtistAndTitle(artist, title)
.orElseThrow(() -> new ResourceNotFoundException("Song not found"));
}
public MusicItem createSong(MusicItem song) {
// Validation logic
return songRepository.save(song);
}
// Other service methods
}
// Data Access Layer
@Repository
public class SongRepository {
private final DynamoDbTable<MusicItem> musicTable;
public SongRepository(DynamoDbEnhancedClient enhancedClient) {
this.musicTable = enhancedClient.table("Music", TableSchema.fromBean(MusicItem.class));
}
public Optional<MusicItem> findByArtistAndTitle(String artist, String title) {
Key key = Key.builder()
.partitionValue(artist)
.sortValue(title)
.build();
MusicItem item = musicTable.getItem(key);
return Optional.ofNullable(item);
}
public MusicItem save(MusicItem song) {
musicTable.putItem(song);
return song;
}
// Other repository methods
}
This layered approach separates concerns and makes your code more maintainable and testable. The API layer handles HTTP-specific concerns, the service layer contains business logic, and the repository layer deals with DynamoDB operations.