Last active
September 15, 2023 18:49
-
-
Save alexanderankin/cb70d8b8afc19040376db7729a9d613d to your computer and use it in GitHub Desktop.
single file application
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
plugins { | |
id 'java' | |
} | |
repositories.mavenCentral() | |
dependencies { | |
implementation platform('org.springframework.boot:spring-boot-dependencies:3.1.3') | |
annotationProcessor platform('org.springframework.boot:spring-boot-dependencies:3.1.3') | |
annotationProcessor 'org.projectlombok:lombok' | |
compileOnly 'org.projectlombok:lombok' | |
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' | |
implementation 'org.springframework.boot:spring-boot-starter-web' | |
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' | |
runtimeOnly 'com.h2database:h2' | |
runtimeOnly 'com.mysql:mysql-connector-j' | |
testImplementation 'org.springframework.boot:spring-boot-starter-test' | |
} | |
tasks.withType(Test).configureEach { useJUnitPlatform() } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.example.SingleFileApplication; | |
import lombok.Data; | |
import lombok.RequiredArgsConstructor; | |
import lombok.experimental.Accessors; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.data.annotation.Id; | |
import org.springframework.data.domain.PageRequest; | |
import org.springframework.data.jdbc.core.JdbcAggregateOperations; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.jdbc.core.BeanPropertyRowMapper; | |
import org.springframework.jdbc.core.JdbcTemplate; | |
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; | |
import org.springframework.web.bind.annotation.*; | |
import org.springframework.web.server.ResponseStatusException; | |
import javax.sql.DataSource; | |
import java.math.BigDecimal; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Optional; | |
import java.util.TreeSet; | |
import java.util.stream.Collectors; | |
import static org.springframework.http.HttpStatus.NOT_FOUND; | |
@SpringBootApplication | |
class SingleFileApplication { | |
public static void main(String[] args) { | |
System.setProperty("spring.datasource.url", | |
"jdbc:h2:mem:single;IGNORECASE=TRUE;MODE=MySQL;"); | |
SpringApplication.run(SingleFileApplication.class, args); | |
} | |
private static <T> T orNotFound(T byId) { | |
return Optional.ofNullable(byId) | |
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND)); | |
} | |
private static void orNotFound(int updated) { | |
if (updated == 0) | |
throw new ResponseStatusException(NOT_FOUND); | |
} | |
@Autowired | |
void setupDb(DataSource dataSource) { | |
DatabasePopulatorUtils.execute(connection -> { | |
var m = new BeanPropertyRowMapper<>(JdbcTable.class); | |
var tables = new ArrayList<JdbcTable>(); | |
var resultSet = connection.getMetaData().getTables(null, null, null, new String[]{"TABLE"}); | |
while (resultSet.next()) { | |
tables.add(m.mapRow(resultSet, 0)); | |
} | |
var tableNames = tables.stream() | |
.filter(j -> "single".equalsIgnoreCase(j.getTableCat())) | |
.map(JdbcTable::getTableName) | |
.collect(Collectors.toCollection(() -> new TreeSet<>(String.CASE_INSENSITIVE_ORDER))); | |
if (tableNames.add("apple")) { | |
connection.createStatement().execute(""" | |
create table apple | |
( | |
-- id IDENTITY NOT NULL PRIMARY KEY, | |
id int unsigned NOT NULL PRIMARY KEY auto_increment, | |
color VARCHAR(255) NOT NULL, | |
weight_lbs decimal(4, 2), | |
variety VARCHAR(50) | |
); | |
"""); | |
} | |
if (tableNames.add("tree")) { | |
connection.createStatement().execute(""" | |
create table tree | |
( | |
id int unsigned NOT NULL PRIMARY KEY auto_increment, | |
name varchar(255) not null unique, | |
description varchar(255) | |
); | |
"""); | |
connection.createStatement().execute(""" | |
alter table apple add column tree_id int unsigned; | |
"""); | |
connection.createStatement().execute(""" | |
alter table apple add foreign key (tree_id) references tree(id); | |
"""); | |
} | |
}, dataSource); | |
} | |
@Data | |
static class JdbcTable { | |
String tableName; | |
@SuppressWarnings("SpellCheckingInspection") | |
String tableSchem; | |
String tableCat; | |
} | |
@Data | |
@Accessors(chain = true) | |
static class Apple { | |
@Id | |
Long id; | |
String color; | |
BigDecimal weightLbs; | |
String variety; | |
Long treeId; | |
} | |
@Data | |
@Accessors(chain = true) | |
static class Tree { | |
@Id | |
Long id; | |
String name; | |
String description; | |
} | |
@RequiredArgsConstructor | |
@RestController | |
@RequestMapping("/apples") | |
static class AppleController { | |
final JdbcTemplate jdbcTemplate; | |
final JdbcAggregateOperations aggregateOperations; | |
@GetMapping | |
List<Apple> all(@RequestParam Optional<Integer> page, | |
@RequestParam Optional<Integer> size) { | |
return aggregateOperations.findAll(Apple.class, | |
PageRequest.of(page.orElse(0), size.orElse(10))).getContent(); | |
} | |
@PostMapping | |
Apple create(@RequestBody Apple apple) { | |
return aggregateOperations.insert(apple); | |
} | |
@GetMapping("/{id}") | |
Apple one(@PathVariable Long id) { | |
return orNotFound(aggregateOperations.findById(id, Apple.class)); | |
} | |
@DeleteMapping("/{id}") | |
@ResponseStatus(HttpStatus.OK) | |
void delete(@PathVariable Long id) { | |
orNotFound(jdbcTemplate.update("delete from apple where id = ?", id)); | |
} | |
// customer just wants to scan a barcode and associate the two | |
@PutMapping("/{id}/tree/{treeId}") | |
@ResponseStatus(HttpStatus.OK) | |
void associateToTree(@PathVariable Long id, @PathVariable Long treeId) { | |
orNotFound(jdbcTemplate.update("update apple set tree_id = ? where id = ?", treeId, id)); | |
} | |
@DeleteMapping("/{id}/tree/{treeId}") | |
@ResponseStatus(HttpStatus.OK) | |
void disassociateFromTree(@PathVariable Long id, @PathVariable Long treeId) { | |
orNotFound(jdbcTemplate.update("update apple set tree_id = null where id = ?", treeId, id)); | |
} | |
} | |
@RequiredArgsConstructor | |
@RestController | |
@RequestMapping("/trees") | |
static class TreeController { | |
final JdbcTemplate jdbcTemplate; | |
final JdbcAggregateOperations aggregateOperations; | |
@GetMapping | |
List<Tree> all(@RequestParam Optional<Integer> page, | |
@RequestParam Optional<Integer> size) { | |
return aggregateOperations.findAll(Tree.class, | |
PageRequest.of(page.orElse(0), size.orElse(10))).getContent(); | |
} | |
@PostMapping | |
Tree create(@RequestBody Tree apple) { | |
return aggregateOperations.insert(apple); | |
} | |
@DeleteMapping("/{id}") | |
@ResponseStatus(HttpStatus.OK) | |
void delete(@PathVariable Long id) { | |
orNotFound(jdbcTemplate.update("delete from tree where id = ?", id)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment