Skip to content

Commit c138cf1

Browse files
committed
Setup Spring example
1 parent 15c48c7 commit c138cf1

31 files changed

Lines changed: 709 additions & 4 deletions

.idea/gradle.xml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Example_1.xml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Example_2.xml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@ Features:
1717

1818
Roadmap:
1919
- Hibernate support library to allow for cloning Hibernate models.
20-
- Spring support for @Transactional support.
2120
- Cleanup to allow for resetting the cloned entity.
2221
- Utility to verify the configuration before running the clone.
2322
- Support for manifests for specifying which tables were cloned in the export. Could also be used to verify the table structure on import.
2423
- Masking sensitive data. Could be done by creating a custom view for the export. Or by using a CSV library like Apache Commons CSV to read and transform the CSV data.
2524

26-
2725
------------------------
2826

2927
# Usage

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ allprojects {
77

88
group = 'dev.mhh.cloner'
99
version = '1.0-SNAPSHOT'
10+
description = 'PostgreSQL Cloner for moving entities between databases'
1011

1112
java {
1213
toolchain {

core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
}
44

55
dependencies {
6-
implementation 'org.postgresql:postgresql:42.7.9'
6+
api 'org.postgresql:postgresql:42.7.9'
77

88
testImplementation 'org.testcontainers:postgresql:1.21.4'
99
testImplementation 'org.testcontainers:junit-jupiter:1.21.4'

example/build.gradle

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
plugins {
2+
id 'java'
3+
id 'org.springframework.boot' version '4.0.2'
4+
id 'io.spring.dependency-management' version '1.1.7'
5+
}
6+
7+
dependencies {
8+
implementation project(':spring')
9+
10+
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
11+
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
12+
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
13+
14+
runtimeOnly 'org.postgresql:postgresql'
15+
16+
compileOnly 'org.projectlombok:lombok:1.18.42'
17+
annotationProcessor 'org.projectlombok:lombok:1.18.42'
18+
19+
testCompileOnly 'org.projectlombok:lombok:1.18.42'
20+
testAnnotationProcessor 'org.projectlombok:lombok:1.18.42'
21+
22+
testImplementation 'org.testcontainers:postgresql:1.21.4'
23+
testImplementation 'org.testcontainers:junit-jupiter:1.21.4'
24+
25+
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
26+
testImplementation 'org.springframework.boot:spring-boot-starter-thymeleaf-test'
27+
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dev.mhh.cloner.example;
2+
3+
import dev.mhh.cloner.spring.SpringConfig;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
import org.springframework.context.annotation.Import;
7+
8+
@SpringBootApplication
9+
@Import(SpringConfig.class)
10+
public class ExampleApplication {
11+
12+
public static void main(final String[] args) {
13+
SpringApplication.run(ExampleApplication.class, args);
14+
}
15+
16+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package dev.mhh.cloner.example.controller;
2+
3+
import dev.mhh.cloner.api.CloneConfiguration;
4+
import dev.mhh.cloner.example.repository.MovableDataRepository;
5+
import dev.mhh.cloner.example.service.RandomMovableDataGenerator;
6+
import dev.mhh.cloner.example.service.MovableDataReader;
7+
import dev.mhh.cloner.spring.SpringCloner;
8+
import jakarta.servlet.http.HttpServletResponse;
9+
import jakarta.transaction.Transactional;
10+
import lombok.AccessLevel;
11+
import lombok.AllArgsConstructor;
12+
import lombok.experimental.FieldDefaults;
13+
import org.springframework.stereotype.Controller;
14+
import org.springframework.ui.Model;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PostMapping;
17+
import org.springframework.web.bind.annotation.RequestParam;
18+
import org.springframework.web.multipart.MultipartFile;
19+
20+
import java.io.IOException;
21+
import java.sql.SQLException;
22+
import java.util.List;
23+
import java.util.UUID;
24+
25+
@Controller
26+
@Transactional
27+
@AllArgsConstructor
28+
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
29+
public class MovableDataController {
30+
SpringCloner cloner;
31+
MovableDataReader movableDataReader;
32+
RandomMovableDataGenerator randomMovableDataGenerator;
33+
MovableDataRepository movableDataRepository;
34+
35+
@GetMapping
36+
public String index(final Model model) {
37+
model.addAttribute("elements", movableDataReader.getAll());
38+
return "index";
39+
}
40+
41+
@PostMapping("/import")
42+
public String importElements(@RequestParam("file") final MultipartFile file) throws IOException, SQLException {
43+
cloner.importClone(file.getInputStream());
44+
return "redirect:/";
45+
}
46+
47+
@PostMapping("/export")
48+
public void exportElements(
49+
@RequestParam(name = "selectedIds", required = false)
50+
final List<UUID> selectedIds,
51+
final HttpServletResponse response
52+
) throws IOException, SQLException {
53+
if (selectedIds == null || selectedIds.isEmpty()) {
54+
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No elements selected");
55+
return;
56+
}
57+
58+
response.setContentType("text/plain");
59+
response.setHeader("Content-Disposition", "attachment; filename=entities.zip");
60+
61+
final var cloneConfiguration = CloneConfiguration.builder()
62+
.table("movable", "id", selectedIds, subTables ->
63+
subTables.joinByJoinTableForeignKey("movable_element", "movable_id"))
64+
.build();
65+
66+
cloner.exportClone(response.getOutputStream(), cloneConfiguration);
67+
}
68+
69+
@PostMapping("/insert-random")
70+
public String insertRandom() {
71+
final var random = randomMovableDataGenerator.generateRandom();
72+
movableDataRepository.save(random);
73+
return "redirect:/";
74+
}
75+
76+
@PostMapping("/clear")
77+
public String clearAll() {
78+
movableDataRepository.deleteAll();
79+
return "redirect:/";
80+
}
81+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dev.mhh.cloner.example.jpa;
2+
3+
import jakarta.persistence.Embeddable;
4+
5+
@Embeddable
6+
public record MovableDataElementJpa(
7+
long sample,
8+
boolean tested
9+
) { }

0 commit comments

Comments
 (0)