{"id":404,"date":"2021-10-17T11:02:31","date_gmt":"2021-10-17T09:02:31","guid":{"rendered":"https:\/\/nissel.it\/?p=404"},"modified":"2021-10-17T17:17:58","modified_gmt":"2021-10-17T15:17:58","slug":"teil-1-rest-service-mit-spring-boot-2-5-und-java-17","status":"publish","type":"post","link":"https:\/\/nissel.it\/index.php\/2021\/10\/17\/teil-1-rest-service-mit-spring-boot-2-5-und-java-17\/","title":{"rendered":"Teil 1: Rest Service mit Spring Boot 2.5 und Java 17"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Motivation<\/h2>\n\n\n\n<p>Ziel ist es einen Baukasten f\u00fcr zuk\u00fcnftige Anwendungen zu erstellen um &#8222;mal eben schnell&#8220; einen Service, Webanwendung, Tool oder ein Test erstellen zu k\u00f6nnen. Das ganze soll mit m\u00f6glichst aktuellen Mitteln umgesetzt werden. <br>Sprint Boot: 2.5.5<br>Java: 17<br>Gradle: 7.2<br>Intellij: 2021.2.3<\/p>\n\n\n\n<p>Dieses Beispiel enth\u00e4lt nur einen H2 In-Memory Datenbank und kein Webfrontend. In sp\u00e4teren Teilen sollen weitere Aspekte hinzukommen.<\/p>\n\n\n\n<div class=\"wp-block-file\"><a href=\"https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/it.nissel.webservice.zip\">it.nissel.webservice<\/a><a href=\"https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/it.nissel.webservice.zip\" class=\"wp-block-file__button\" download>Herunterladen<\/a><\/div>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Einrichtung<\/h2>\n\n\n\n<p>Am einfachsten ist es \u00fcber den <a href=\"https:\/\/start.spring.io\/\">Spring Initializr<\/a> ein neues Projekt zu erstellen mit allen gew\u00fcnschten dependencies.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"533\" src=\"https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1-1024x533.png\" alt=\"Spring Initializr dependecies\" class=\"wp-image-406\" title=\"\" srcset=\"https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1-1024x533.png 1024w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1-300x156.png 300w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1-768x399.png 768w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1-1536x799.png 1536w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1.png 1544w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">JPA Entity erstellen<\/h2>\n\n\n\n<p>Mit der @Entity Annotation wird eine Java Klasse zu einer Datenbank Mapping Klasse. Mit @Id und @GeneratedValue wird eine Automatisch generierte ID erstellt. Die @Data Annotation von Lombok erstellt automatisch getter(), setter() und weitere Methoden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Data\n@Entity\npublic class Transaction {\n    @Id\n    @GeneratedValue(strategy = GenerationType.AUTO)\n    private long id;\n    private String summary;\n    private float value;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Zugriff auf die Daten \u00fcber ein Repository<\/h2>\n\n\n\n<p>Ein sehr einfacher Weg Rest Methoden f\u00fcr eine Datenklasse zur Verf\u00fcgung zu stellen ist mit der @RepositoryRestResource Annotation. Dadurch wird ein ganzer Service mit create, delete und update Methoden erstellt. Auch das implementieren von findBy() Methoden ist sehr einfach m\u00f6glich. In dem Beispiel reicht es nur die Methode im Interface zu deklarieren um nach einem Attribut in der Tabelle zu suchen. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RepositoryRestResource(collectionResourceRel = \"banking\", path = \"banking\")\npublic interface TransactionRepository extends PagingAndSortingRepository&lt;Transaction, Long&gt; {\n\n    List&lt;Transaction&gt; findBySummary(@Param(\"summary\") String summary);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">JUnit Tests<\/h2>\n\n\n\n<p>Das Testen des  TransactionRepository  kann \u00fcber die @SpringBootTest und @Autowired Annotation einfach getestet werden. Spring bietet aber auch die M\u00f6glichkeit das Restservice Verhalten mit zu testen. Dazu muss ein MockMvc eingebunden werden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@SpringBootTest\n@AutoConfigureMockMvc\npublic class AccessingDataRestApplicationTests {\n\n    private static final String TRANSACTION_JSON = \"\"\"\n            {\"summary\": \"B\u00e4cker\", \"value\": 5.49}\n            \"\"\";\n\n    @Autowired\n    private MockMvc mockMvc;\n\n    @Autowired\n    private TransactionRepository personRepository;\n\n    @BeforeEach\n    public void deleteAllBeforeTests() throws Exception {\n        personRepository.deleteAll();\n    }\n\n    @Test\n    public void shouldReturnRepositoryIndex() throws Exception {\n\n        mockMvc.perform(get(\"\/\"))\n                .andDo(print())\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$._links.banking\").exists());\n    }\n\n    @Test\n    public void shouldCreateEntity() throws Exception {\n\n        mockMvc.perform(post(\"\/banking\").content(TRANSACTION_JSON))\n                .andExpect(status().isCreated())\n                .andExpect(header().string(\"Location\", containsString(\"banking\/\")));\n    }\n\n    @Test\n    public void shouldRetrieveEntity() throws Exception {\n        String location = createTransaction();\n        mockMvc.perform(get(location))\n                .andDo(print())\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.summary\").value(\"B\u00e4cker\"))\n                .andExpect(jsonPath(\"$.value\").value(5.49));\n    }\n\n    @Test\n    public void shouldQueryEntity() throws Exception {\n        createTransaction();\n\n        mockMvc.perform(get(\"\/banking\/search\/findBySummary?summary={summary}\", \"B\u00e4cker\"))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$._embedded.banking&#91;0].summary\").value(\"B\u00e4cker\"))\n                .andExpect(jsonPath(\"$._embedded.banking&#91;0].value\").value(\"5.49\"));\n    }\n\n    @Test\n    public void shouldUpdateEntity() throws Exception {\n        String location = createTransaction();\n\n        mockMvc.perform(put(location).content(\"{\\\"summary\\\": \\\"B\u00e4cker-Changed\\\"}\"))\n                .andExpect(status().isNoContent());\n\n        mockMvc.perform(get(location)).andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.summary\").value(\"B\u00e4cker-Changed\"))\n                .andExpect(jsonPath(\"$.value\").value(0.0));\n    }\n\n    @Test\n    public void shouldPartiallyUpdateEntity() throws Exception {\n        String location = createTransaction();\n\n        mockMvc.perform(patch(location).content(\"{\\\"summary\\\": \\\"B\u00e4cker-Changed\\\"}\"))\n                .andExpect(status().isNoContent());\n\n        mockMvc.perform(get(location))\n                .andExpect(status().isOk())\n                .andExpect(jsonPath(\"$.summary\").value(\"B\u00e4cker-Changed\"))\n                .andExpect(jsonPath(\"$.value\").value(5.49));\n    }\n\n    @Test\n    public void shouldDeleteEntity() throws Exception {\n        String location = createTransaction();\n        mockMvc.perform(delete(location)).andExpect(status().isNoContent());\n\n        mockMvc.perform(get(location)).andExpect(status().isNotFound());\n    }\n\n    private String createTransaction() throws Exception {\n        MvcResult mvcResult = mockMvc.perform(post(\"\/banking\").content(TRANSACTION_JSON))\n                .andExpect(status().isCreated()).andReturn();\n\n        String location = mvcResult.getResponse().getHeader(\"Location\");\n        Assertions.assertNotNull(location);\n        return location;\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Eigener Controller<\/h2>\n\n\n\n<p>F\u00fcr spezielle Anwendungsf\u00e4lle kann nun ein eigener Controller verwendet werden. In diesem Beispiel werden zwei Transaktionen zusammen gelegt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@RestController\npublic class TransactionController {\n\n    @Autowired\n    private TransactionRepository transactionRepository;\n\n    @GetMapping(\"\/mergeTransactions\")\n    public ResponseEntity&lt;Transaction> mergeTransactions(@RequestParam(value = \"id1\") long id1, @RequestParam(value = \"id2\") long id2) {\n        Optional&lt;Transaction> transaction1Optional = transactionRepository.findById(id1);\n        Optional&lt;Transaction> transaction2Optional = transactionRepository.findById(id2);\n        if (transaction1Optional.isPresent() &amp;&amp; transaction2Optional.isPresent()) {\n            Transaction transaction1 = transaction1Optional.get();\n            Transaction transaction2 = transaction2Optional.get();\n\n            transaction1.setSummary(transaction1.getSummary() + \" + \" + transaction2.getSummary());\n            transaction1.setValue(transaction1.getValue() + transaction2.getValue());\n\n            transaction1 = transactionRepository.save(transaction1);\n            transactionRepository.delete(transaction2);\n\n            \/\/TODO missing error handling\n\n            return ResponseEntity.ok(transaction1);\n        }\n        return ResponseEntity.notFound().build();\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Controller Test<\/h2>\n\n\n\n<p>Der Controller kann \u00fcber @Autowired im Testfall eingebunden werden und sehr einfach getestet werden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@SpringBootTest\nclass TransactionControllerTest {\n\n    @Autowired\n    private TransactionController transactionController;\n\n    @Autowired\n    private TransactionRepository transactionRepository;\n\n    @Test\n    void mergeTransactions() {\n        Transaction transaction1 = new Transaction();\n        transaction1.setSummary(\"Apples\");\n        transaction1.setValue(1.5f);\n        transaction1 = transactionRepository.save(transaction1);\n\n        Transaction transaction2 = new Transaction();\n        transaction2.setSummary(\"Bananas\");\n        transaction2.setValue(3.5f);\n        transaction2 = transactionRepository.save(transaction2);\n\n        ResponseEntity&lt;Transaction> merged = transactionController.mergeTransactions(transaction1.getId(), transaction2.getId());\n        Transaction mergedTransaction = merged.getBody();\n\n        Assertions.assertNotNull(mergedTransaction);\n        Assertions.assertEquals(transaction1.getId(), mergedTransaction.getId());\n        Assertions.assertEquals(\"Apples + Bananas\", mergedTransaction.getSummary());\n        Assertions.assertEquals(5f, mergedTransaction.getValue());\n\n    }\n\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Gradle 7.2 in Intellij<\/h2>\n\n\n\n<p>Die Download URL von Gradle 7.2 musste in der gradle-wrapper.properties manuell angegeben werden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper\/dists\ndistributionUrl=https:\/\/services.gradle.org\/distributions\/gradle-7.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper\/dists\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Java 17 in Intellij<\/h2>\n\n\n\n<p>Intellij unterst\u00fctzt bereits Java 17. Es kann auch direkt \u00fcber die IDE herunter geladen werden. Noch warnt die IDE davor, da experimentell in der IDE ist. Ich musste die Sprachfeatures manuell auf Java 17 stellen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"845\" src=\"https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1024x845.png\" alt=\"Intellij project settings\" class=\"wp-image-405\" srcset=\"https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-1024x845.png 1024w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-300x248.png 300w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik-768x634.png 768w, https:\/\/nissel.it\/wp-content\/uploads\/2021\/10\/grafik.png 1030w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Quellen<\/h2>\n\n\n\n<p><a href=\"https:\/\/spring.io\/guides\/gs\/rest-service\/\">https:\/\/spring.io\/guides\/gs\/rest-service\/<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/spring.io\/guides\/gs\/accessing-data-rest\/\">https:\/\/spring.io\/guides\/gs\/accessing-data-rest\/<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/start.spring.io\/\">https:\/\/start.spring.io\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Motivation Ziel ist es einen Baukasten f\u00fcr zuk\u00fcnftige Anwendungen zu erstellen um &#8222;mal eben schnell&#8220; einen Service, Webanwendung, Tool oder ein Test erstellen zu k\u00f6nnen. Das ganze soll mit m\u00f6glichst aktuellen Mitteln umgesetzt werden. Sprint Boot: 2.5.5Java: 17Gradle: 7.2Intellij: 2021.2.3 Dieses Beispiel enth\u00e4lt nur einen H2 In-Memory Datenbank und kein Webfrontend. In sp\u00e4teren Teilen sollen &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/nissel.it\/index.php\/2021\/10\/17\/teil-1-rest-service-mit-spring-boot-2-5-und-java-17\/\" class=\"more-link\"><span class=\"screen-reader-text\">\u201eTeil 1: Rest Service mit Spring Boot 2.5 und Java 17\u201c <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[21,16,20],"class_list":["post-404","post","type-post","status-publish","format-standard","hentry","category-softwareentwicklung","tag-intellij","tag-java","tag-spring"],"_links":{"self":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts\/404","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/comments?post=404"}],"version-history":[{"count":3,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts\/404\/revisions"}],"predecessor-version":[{"id":411,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts\/404\/revisions\/411"}],"wp:attachment":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/media?parent=404"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/categories?post=404"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/tags?post=404"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}