SAPUI5 Custom Controls – Teil 2

SAPUI5 Custom Controls – Teil 2

SAPUI5 Custom Control Teil 2

Der Weg zum eigenen Control

Da wir nun im letzten Teil alles über den generellen Aufbau eines SAPUI5 Custom Controls gelernt haben, möchten wir das ganze in der Praxis anwenden.

 

Wie lege ich im SAPUI5 Custom Control eine Liste von Personen?

Unsere Aufgabenstellung ist folgende:

Wir möchten einen Weg finden, um eine Liste von Personen gemäß hypothetischen Firmen-Design-Guidelines darzustellen. Leider reicht das UI5 Framework für die angenommene Einschränkung nicht aus.

Daher benötigen wir ein Custom Control. Genauer gesagt, benötigen wir folgende Komponenten:

  • PersonList – Darstellung unserer Liste
  • PersonListItem – Eine einzelne Zeile
  • style.css – Custom Styling

Optional können noch folgende Files angelegt werden:

  • PersonListRenderer – Zum ausgelagerten Rendern der Liste
  • PersonListItemRenderer – Zum ausgelagterten Rendern eines Items

PersonListItem im SAPUI5 Custom Control

Wir arbeiten uns von unten nach oben. D.h. wir fangen mit unserem aggregierten Control an, welches eben das einzelne Listitem wäre.

Ausgeben möchten wir Vorname, Nachname und das Alter der Person. Um noch ein Custom-CSS reinzubekommen, werden wir Personen mit einem Alter geringer als 18 anders formattieren.

Erstellen wir zunächst ein neues UI5 Projekt und legen die Files für die Custom-Controls an.

Anschließend definieren wir ein Template für das PersonListItem:

Wir wissen, dass wir mit sap.ui.define ein neues Modul anlegen, das zur Laufzeit dynamisch von unserem UI5 Framework geladen wird. Und genau so definieren wir eben auch ein Custom Control. Dieses inkludiert das sap.ui.core.Control, welches wir per .extend erweitern und zurückliefern.

Unsere PersonListItem soll 3 verschiedene Personaleigenschaften ausgeben. Daher brauchen wir 3 Properties (Vorname, Nachname, Alter).

Aggregations soll unser PersonListItem nicht haben, denn wir möchten das PersonListItem selbst als zu aggregierendes Control verwenden.

Properties anlegen

Wir benötigen 3 Properties:

  • firstName: string
  • lastName: string
  • age: int

Auf die Properties kann per get<PropertyName>() zugegriffen werden.

 

Events anlegen

Wir möchten, dass bei einem Click auf das PersonListItem ein Event ausgeführt wird. Dieses liefert eine Hallo-Message zurück.

  • sayHello
    • parameter: message(string)

Anschließend definieren wir eine Eventhandler für das HTML-onclick-Event. Falls dieses auftritt, triggern wir das sayHello-Event und liefern eine Message bestehend aus Name und „says hello“ per message-Parameter zurück.

 

CSS Styling

Wir möchten unsere 3 Properties per HTML-ul-Element anzeigen. Da ist jedoch das Problem, dass die Liste standartmäßig untereinander ihre Items anordnet. Um das zu umgehen, sagen wir per CSS-Klasssenselektor, das alle li-Elemente unter der Klasse die CSS-Property display mit dem Wert inline bekommen.

Und zusätzlich definieren wir noch ein padding von 10 Pixel und die Schriftfarbe black;

 

Renderer

Der Renderer ist das Herzstück unseres Controls. Hier wird definiert, was wann und wie in das DOM eingtragen werden soll.

Die wichtigsten Renderer-Funktionen sind dem letzten Blog-Teil zu entnehmen.

Wir möchten unser Control als div-Element und einer ul ausgeben. Zusätzlich möchten wir noch das Alter dementsprechend farblich hervorheben, basierend auf dem Kriterium, dass das Alter > 18 ist.

1: div ausgeben

Zu aller erst erstellen wir per openStart ein öffnendes div-Tag. Dieses div-Tag wird per writeControlData die Control-Referenz geschrieben und anschließend fügen wir noch unsere CSS-Klasse hinzu. Dann wird das öffnende div-Tag mit > geschlossen.

2: Liste aufbauen

Wir erstellen eine neue unordered-list. Als erstes List-Item geben wir den Vornamen der Person aus und dann den Nachnamen.

3: Alter ausgeben

Je nach dem ob die Person ein Alter > 18 hat, erstellen wir ein List-Item mit dem Style colorred oder color: green.

4: Alles schließen

Zu letzt schließen wir die Liste und abschließend das div-Tag.

 

Somit ist unser PersonListItem komplett. Im nächsten Schritt werden wir nun die PersonList definieren und unser PersonListItem aggregieren.

PersonList im SAPUI5 Custom Control

Die PersonList soll einen Titel bekommen und unsere Personen per PersonListItem anzeigen.

Hierfür benötigen wir Properties und Aggregations.

Metadata definieren

Wir benötigen folgende Property für unseren Titel:

  • title (string) – default: Persons

Zusätzlich möchten wir noch PersonListItems aggregieren:

  • items (PersonListItem) – multiple: true

CSS Styling

per list-style: none sagen wir, dass unsere Personenliste keine Aufzählungszeichen haben sollte.

Dann selektieren wir alle Items mit einem ungeraden Index und färben diese grau ein.

Zusätzlich geben wir noch ein seitliches margin von 15 Pixel.

Renderer definieren

In unserem Renderer wollen wir den Titel der Personenliste und aggregiert die PersonListItems ausgeben.

1: div öffnen

Gleich wie beim PersonListItem öffnen wir einen div-Container und vergeben CSS-Klasse und Control-Data.

2: Titel ausgeben

Der Titel wird innerhalb eines h2-Tags ausgebeben.

3: Items-Aggregation rendern

Per getAggregation(„items“) können wir uns den Inhalt der Items-Aggregation in einem Array speichern. Dieses Array laufen wir durch und erstellen für jeden Eintrag ein neues li-Tag mit der myPersonListItemSelector-Klasse. Dieses bekommt ihren Inhalt per renderControl()-Funktion, welche die render-Methode des übergebenen Controls aufruft. In unserem Fall ist das das PersonListItem.

4: alles schließen

 Abschließend fügen wir noch die schließenden ul- und div-Tags hinzu.

Jetzt sind unsere Custom-Controls fertig und können in den Views verwendet werden.

Controls verwenden

Die beiden Custom Controls können nun in unserer View verwendet werden.

1: Namespace einbinden

Um Controls aus einem anderen Namensraum einzubinden, muss für diesen ein Alias angelegt werden. Wir verweisen hier auf unseren Folder, wo wir die Controls angelegt haben.

2: PersonList einfügen

Wir legen eine neue PersonList an. Dort vergeben wir die title-Property und legen eine items-Aggregation an.

3: PersonListItems hinzufügen

In der items-Aggregation legen wir nun mehrere PersonListItems an. Das würde natürlich auch über Aggregation-Binding in der PersonList per Model funktionieren.

Wir legen aber z.B. 3 PersonListItems an und vergeben allen Properties Werte. Zusätzlich definieren wir eine Funktion für unser sayHello-Event.

Dieses müssen wir dann noch im zugehörigen Controller ausprogrammieren.

Also definieren wir eine neue Methode namens onSayHelloHandler, wo wir den Eventparameter message auslesen und per alert anzeigen.

App ausführen

Unsere App ist nun funktionsfähig und zeig unsere Custom-Controls richtig an.

Wir sehen nun unseren Titel und 3 darunterliegende Personen, die farblich abwechselnd hinterlegt sind.

Zusammenfassung

In 2 Blogteilen haben wir nun alles Notwendige gelernt, um mit dem Entwickeln von eigenen Custom-Controls durchzustarte, falls die Anforderungen nicht durch das umfangreiche SAPUI5 Controls-Paket abgedeckt wird.

Eine weitere Vorgehensweise könnte sein, meine eigenen Custom-Controls in eine UI5-Library/Git Repo zu packen und zu deployen. Dann kann ich diese in weiteren Applikationen verwenden und kann mir meine eigene, umfangreiche Library bauen.

Nur sollte man nicht für jede Anforderung ein neues Control entwickeln oder ein bestehendes zu erweitern. Man sollte so gut und so weit wie möglich mit dem arbeiten, was einem das SAPUI5 Framework zur Verfügung stellt und sich so weit wie möglich in die Materie einarbeiten.

Wer also gerne mehr bezüglich UI5 wissen möchte, kann sich gerne unseren zahlreichen Blogs bedienen.

Die interessantesten für das Controls-Thema sind:

Vielen Dank für das Lesen dieser Blogreihe und wenn Fragen auftreten, können sie gerne in den Kommentaren gestellt werden.

Data Binding in SAPUI5

Data Binding in SAPUI5

Übersicht

In diesem Blog werden wir uns mit Data Binding in SAPUI5, Open UI5 und SAP Fiori Apps beschäftigen.

Die Inhalte werden in unseren SAP Trainings bis ins kleinste Detail behandelt. Diese Trainings laufen unter den Titeln HOUI5 und HOFIO und werden bei SAP im offiziellen Schulungskatalog für Deutschland, Österreich und der Schweiz gelistet.

Inhalt

In diesem Blog zeigen wir Ihnen folgende Themen: 

  • Grundlegendes zum Data Binding
  • Databinding Modi
    • One-Time-Binding
    • One-Way-Binding
    • Two-Way-Binding
  • Arten von Databindings
    • Property Binding
    • Aggregation Binding
    • Element Binding
  • Databinding Syntax
    • Binding Pfad
    • Composite Binding
    • Expression Binding
    • Metadata Binding

Grundlegendes zum Data Binding in SAPUI5

In SAPUI5 und OpenUI5 können Sie das Data Binding einsetzen, um Daten aus dem Model unter Verwendung von UI-Controls darzustellen. Das ermöglicht Ihnen auch das Aktualisieren und Editieren der Daten direkt im User Interface, dadurch verknüpfen Sie das User Interface mit dem Model.

Hierfür benötigen Sie folgende zwei Dinge:

  • Ein Data Model (JSON, OData)
  • Eine Bindinginstanz

Als Data Model verwenden Sie üblicherweise ein JSON- oder ein OData-Model. Das Model hält für Sie die Daten vor und bietet Ihnen Methoden an , um mit dem Server zu kommunizieren. Zusätzlich bietet es Ihnen Möglichkeiten, um Data Bindings programmatisch zu erstellen. Wenn Sie die Methode aufrufen sorgt es dafür, dass eine Binding Instanz für Sie erstellt wird. Die Instanz beinhaltet Informationen zum Binding sowie bestimmte Events. Ein spezieller Event wird getriggert, wenn sich die gebundenen Daten ändern.

Welche Data Binding Modi im SAPUI5 gibt es?

Durch die Enumeration „sap.ui.model.BindingMode“ werden Ihnen drei verschiedene Data Binding Modi bereitgestellt. Diese können Sie unter Verwendung der Methode „.setDefaultBindingMode(bindingMode)“ allgemein auf ein Model anwenden. Optional können Sie es über das Property „oBindingInfo.mode“ auch auf einzelne Binding Instanzen verwenden.

One-Time-Binding – sap.ui.model.BindingMode.OneTime

Setzen Sie den BindingMode auf „OneTime“, werden die Daten einmalig vom Model gelesen. Ausserdem können Sie nicht manipuliert werden.

One-Way-Binding – sap.ui.model.BindingMode.OneWay

“OneWay”-Binding bedeutet, dass die Daten vom Model gelesen und an die View gebunden werden. Die Daten können Sie in der View zwar manipulieren, aber leider können Sie die Änderungen nicht zurück ins Model übertragen. Dieser Binding Modus war für das JSON-Model lange Zeit das Standardverhalten.

Two-Way-Binding – sap.ui.model.BindingMode.TwoWay

Möchten Sie in der View Daten manipulieren und die Änderungen automatisch in das Model übernehmen? Dann ist der Binding Mode „TwoWay“ genau das Richtige für Sie. Im Gegensatz zum One-Way-Binding können Sie Daten vom Model zur View und umgekehrt übertragen.

Arten von Data Bindings in SAPUI5 und OpenUI5

In SAPUI5 und OpenUI5 gibt es drei unterschiedliche Arten von Data Bindings. Wie sie damit am arbeiten, führen wir im Folgenden Beitrag für Sie näher aus. Im Data Binding müssen Sie immer einen Bindingpfad angeben. Diesen sollen Sie durch ‚{}‘ kennzeichnen

Je nachdem, ob Sie das Default-Model oder ein Named-Model verwenden, unterscheidet sich dieser Pfad geringfügig.  Wenn Sie ein  Named-Models verwenden wollen, müssen dem Namen der Property noch ein ‚modelName>‘ voranstellen.

Möchten Sie die Eigenschaft „FirstName“ auf die value-Eigenschaft eines Inputs binden (Property Binding), sieht das, bei der Verwendung eines Default-Models, wie folgt aus:

Verwenden Sie hingegen ein Named-Model, müssen Sie der Eigenschaft der Modelname voranstellen:

Property Binding

Verwenden Sie ein Property Binding, um beispielsweise Daten in einem Formular zu binden. Hierfür müssen Sie in der entsprechenden Eigenschaft (z.B.: text, value, …) einen Binding-Pfad angeben.

Am Propertynamen vorangestellten ‚/‘ erkenne Sie, dass es sich um einen absoluten Pfad handelt. Das bedeutet, das sich die Eigenschaft „FirstName“ auf der Root-Ebene des Default-Models befindet.

Aggregation Binding

Möchten Sie Listen auf ein UI-Control binden, kommt das Aggregation Binding zum Einsatz. Es wird meist in Listen und Tabellen verwendet, dabei müssen Sie auch ein Template angeben. Dieses wird für jedes Element in der Liste geklont. Innerhalb des Templates müssen Sie relative Pfade verwenden, da der Pfad des Aggregation Bindings bereits auf die entsprechende Liste verweisen. Anstelle eines Templates haben Sie auch die Option eine Factory-Function anzugeben. Die definiert, wie die einzelnen Einträge im User Interface dargestellt werden sollen.

Default-Model

Named-Model

Element Binding

Das Element Binding ermöglicht Ihnen das Binden einzelner Elemente einer Liste auf UI-Controls. Hierbei wird ein sogenannter Binding-Context erzeugt. Die Binding-Pfade müssen Sie immer relativ angeben.

Dieses SAPUI5 Data Binding können Sie überwiegend in Master-Detail Fiori Apps einsetzen.

Das Element-Binding können Sie auf folgende Arten umsetzen:

  • Aufrufen der Methode bindElement im Controller
  • Verwenden einer Binding-Property des Controls

Data Binding Syntax

Mit einem Binding-Pfad können Sie ein UI-Control auf die Daten des Models binden.  Wenn sie durch das definieren eines Pfades, erstellen Sie einen Binding-Context.

Composite Binding

Wenn Sie einen Formatter verwenden, ist es häufig der Fall, dass mehrere Werte an den Formatter übergeben werden müssen. Diese Anforderung können Sie mit einem Composite-Binding umsetzen. In der jeweiligen Eigenschaft (value, text, …) des UI-Control kann das parts-Array, eine Liste paths definieren.

Welche Arten von Expression Binding gibt es ?

Mithilfe des Expression-Binding können Sie simple Prüfungen – z.B.: Vergleich von Werten – direkt in der View zur Laufzeit durchführen. Dadurch ersparen Sie sich das Implementieren von zusätzlichen Formatter-Funktionen im Controller.

Sie können das Expression-Binding auf 2 Arten umsetzen:

  • ‚{=expression}‘: Es wird ein One-Way-Binding verwendet. Sollten sich Werte im Model ändern, wird auch das Binding aktualisiert.
  • ‚{:=expression}‘: Es wird ein One-Time-Binding verwendet. Der Wert wird einmalig ermittelt und anschließend nicht mehr aktualisiert.

Als expression können Sie beliebige Prüfungen implementieren. Dabei ist die Syntax ähnliche jener in Javascript, jedoch werden nicht alle JavaScript Expressions unterstützt. Innerhalb der Expression greifen Sie auf Modeldaten folgendermaßen zu: ‚${binding}‘ oder ‚%{binding}‘

Eine Prüfung und entsprechende Anzeige verschiedener Werte sieht wie folgt aus:

${binding} VS %{binding}

Verwenden Sie ‚${binding}‘ wird der Wert automatisch in den Typ der Eigenschaft des Controls konvertiert. Im Falle des Visible-Properties eines sap.m.Input Controls wäre dies der Typ ‚boolean‘. Wenn Sie nicht immer ist diese Konvertierung erwünschen, können Sie diese mit ‚%{binding}‘ umgehen. ‚%{binding}‘ ist eine Kurzform für ‚${path: ´binding´, targetType: ´any´}‘. Den targetType können Sie durch alle Typen, die sap.ui.model.odata.type bietet ersetzen.

Metadata Binding

Möchten Sie die Metadata-Eigenschaften wie sap:label, sap:createable, sap:updatable, … in der View abfragen, können Sie dies mit einem Metadata-Binding erreichen.

Auch hier können Sie zwei Ansätze verwenden.

Absolute Bindings

Absolute Bindings sehen folgendermaßen aus:

/#EntityName/PropertyName/@Attribut

Relative Bindings

Nun zeige ich ihnen wie Relative Bindings aussehen:

/EntitySetName/PropertyName/#@Attribut

Best-Practice Beispiel zum Data Binding in SAPUI5 mit OData-Model und Two-Way-Binding

Abschließend hab ich noch für Sie ein kurzes Best-Practice Beispiel vorbereitet. Wir schicken mit Hilfe eines OData-Models und Two-Way-Binding ein Update auf die Details eines Employees.

Hier können sie folgende Methoden verwenden:

  • hasPendingChanges: Zum Abfragen, ob es Änderungen gibt
  • submitChanges: Mit ‚Save‘ werden Sie die Änderungen ans Backend schicken
  • resetChanges: Mit ‚Discard‘ werden Sie die Änderungen verwerfen

View:

Controller:

Bei unserem Blog über Data Binding in SAPUi5 haben sie nun viele Informationen bekommen. Wenn sie an weiteren Themen interessiert sind, klicken sie hier unten auf GIT oder SAPUI5 Custom Controls. 

Wollen Sie, dass  wir uns um Ihre Szenarien kümmern? Nehmen Sie gerne Kontakt auf den  klassische SAP Beratung und SAP Cloud Consulting liegt uns .Egal ob Deutschland, Schweiz oder Österreich – unser Team bestehend aus erfahrerenen Beratern und Entwicklern ist gerne für Sie da!

Wir lassen sie nicht in der Cloud hängen den SAP ( Cloud ) Consulting liegt in unsere DNA

SAP Cloud Platform Java Development – Part 2

SAP Cloud Platform Java Development – Part 2

Access SAP HANA in the NEO environment

In the first part of the blog series I introduced you to Java development based on the Spring framework. Using a simple RestController the basics were shown.

What would an application be without access to a database. In the SAP Cloud context it is obvious that SAP HANA is used as database. In this part of the blog series I show you how to access the database in the NEO environment.

JNDI – The yellow pages

The HANA database is accessed via JNDI, in my opinion one of the most exciting technologies in the Java environment. With a JNDI lookup resources managed by the runtime environment can be loaded. The idea behind it is very simple – the runtime environment takes care of instantiating the required classes and makes them available to the application.

In order for the application presented here to support JNDI, some activities must be carried out. In the first step it is necessary to create a subdirectory called webapp in the directory src > main and in this subdirectory a subdirectory called WEB-INF. In the directory src > main > webapp > WEB-INF it is only necessary to create a file named web.xml.

Folderstructure

Folderstructure für web.xml

Those resources that are managed by JNDI must be listed in the web.xml file as resource reference (resource-ref). The name (res-ref-name) under which the resource is addressed and the underlying Java class (res-type) must be defined.

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 version="3.0">
   <resource-ref>
     <res-ref-name>jdbc/DefaultDB</res-ref-name>
     <res-type>javax.sql.DataSource</res-type>
   </resource-ref>
</web-app>

Declare Spring Data Dependency

The first step has already been completed. Since we do not implement the access to the database with SQL commands ourselves, but use Spring Data, we have to define a corresponding dependency in pom.xml.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Since Spring Data supports a variety of databases, it is necessary to tell the runtime environment which database to use. This is done using the application.properties file. If this file does not yet exist, it must be created in the directory src > main > resources. The fully qualified class name of the HANA driver is com.sap.db.jdbc.Driver

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.HANAColumnStoreDialect
spring.jpa.properties.hibernate.connection.pool_size = 10
spring.datasource.driverClassName=com.sap.db.jdbc.Driver

@Configuration

Spring @Configuration annotation is part of the Spring Core Framework. The Spring annotation indicates that the class has @Bean definition methods. This allows the Spring container to process the class and generate Spring Beans at runtime that can be used in the application.

For use in the Neo Stack, the data source must be loaded using JNDI. A corresponding class must be created for this. The following code snippet shows the complete class.

Neo Datasource Configuration

package at.clouddna.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import javax.sql.DataSource;
import java.sql.SQLException;
@Configuration
@Profile({"neo"})
public class NeoConfig 
{	
	@Bean(destroyMethod="")
	public DataSource jndiDataSource() throws IllegalArgumentException, SQLException
	{
		JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
		DataSource ds = dataSourceLookup.getDataSource("java:comp/env/jdbc/DefaultDB");
		return ds;
	}
}
The JNDI lookup aims at the name java:comp/env/jdbc/DefaultDB. The prefix java:comp/env/ is always the same in the SAP Cloud Platform. The name jdbc/DefaultDB defined behind it corresponds to the res-ref-name in the web.xml

Development of an entity class

The use of Spring-Data allows a very efficient development of the persistence layer. Spring-Data is based on the Hibernate Framework. As soon as we create a class and assign the annotation @Entity to it, a corresponding table is created in the underlying database. In the following code snippet I show you a simple user class.

User.java

package at.clouddna.demo.model;
import javax.persistence.*;
@Entity
public class User {
    @Column(nullable = false)
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    private String firstname;
    private String lastname;
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }
    public String getFirstname() {
        return this.firstname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    public String getLastname() {
        return this.lastname;
    }
}

CRUD Methods

The great thing about Spring-Data is the out-of-the-box availability of CRUD methods. All you have to do is create an interface that inherits from the JpaRespository. This is shown for the user entity in the following code snippet.

package at.clouddna.demo.repository;
import at.clouddna.demo.model.User;
public interface IUserRepository extends JpaRepository<User, Long> {
}
The repository can now be used directly in the controller by accessing it via autowiring. However, my company refrains from this. We always create an associated DTO (Data Transfer Object) for each entity class and additionally create a service class that is provided with the @Service Annotation, which encapsulates the use of the repository. The service class can be injected in the controller via the @Autowired Annotation.

Of course I will show you how it works.

ServiceBase Classs and Model Mapper

The mapping of the Entity class to the DTO and vice versa is not done manually but with ModelMapper. The modelmapper must be included in pom.xml as a dependency.

<dependency>
   <groupId>org.modelmapper</groupId>
   <artifactId>modelmapper</artifactId>
   <version>2.3.5</version>
</dependency>

ServiceBase.java

package at.clouddna.codegenerator.service.da;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public abstract class ServiceBase {
 private ModelMapper modelMapper;
 public ServiceBase(){
 this.modelMapper = new ModelMapper();
 this.modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STANDARD);
 }
 public <D, T> D map(T entity, Class<D> outClass) {
 return modelMapper.map(entity, outClass);
 }
 public <D, T> List<D> mapAll(Collection<T> entityList, Class<D> outCLass) {
 return entityList.stream()
 .map(entity -> map(entity, outCLass))
 .collect(Collectors.toList());
 }
 protected void writeToFile(String fileName, String content) throws IOException {
 FileOutputStream outputStream = new FileOutputStream(fileName);
 byte[] strToBytes = content.getBytes();
 outputStream.write(strToBytes);
 outputStream.close();
 }
 protected void writeToFile(String fileName, byte[] content) throws IOException {
 FileOutputStream outputStream = new FileOutputStream(fileName);
 outputStream.write(content);
 outputStream.close();
 }
} 

Service Class

The Service class inherits from the ServiceBase class and encapsulates access to the database. The following code snippet shows the UserService class. It is important that the class is provided with the annotation @Service. This allows it to be used in the controller via autowiring.

UserService.java

package at.clouddna.demo.service;
import at.clouddna.demo.dto.UserDto;
import at.clouddna.demo.model.User;
import at.clouddna.demo.respository.IUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserService extends ServiceBase {
 @Autowired
 private IUserRepository repository;
 public UserDto create(UserDto userDto) {
 return map(repository.save(map(userDto, User.class)), UserDto.class);
 }
 public UserDto update(UserDto userDto) {
 return map(repository.save(map(userDto, User.class)), UserDto.class);
 }
 public boolean delete(Long id) {
 repository.deleteById(id);
 return true;
 }
 public UserDto findById(Long id) {
 Optional<User> userOptional = repository.findById(id);
 if(!userOptional.isPresent()) {
 return null;
 }
 return map(userOptional.get(), UserDto.class);
 }
 public List<UserDto> findAll() {
 return mapAll(repository.findAll(), UserDto.class);
 }
}

RestController

Finally, I will show you how the previously developed service in the RestController can be fully used for all CRUD methods. You will be surprised how easy it is!

UserController.java

package at.clouddna.demo.controller;
import at.clouddna.demo.dto.UserDto;
import at.clouddna.demo.model.User;
import at.clouddna.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
 @Autowired
 private UserService userService;
 @GetMapping
 public ResponseEntity getAll() {
 return ResponseEntity.ok(userService.findAll());
 }
 @GetMapping("/{id}")
 public ResponseEntity getById(@PathVariable("id") Long id) {
 return ResponseEntity.ok(userService.findById(id));
 }
 @PostMapping
 public ResponseEntity create(@RequestBody UserDto body) {
 return ResponseEntity.ok(userService.create(body));
 }
 @PutMapping("/{id}")
 public ResponseEntity update(@PathVariable("id") Long id,
 @RequestBody UserDto body) {
 body.setId(id);
 return ResponseEntity.ok(userService.update(body));
 }
 @DeleteMapping("/{id}")
 public ResponseEntity delete(@PathVariable("id") Long id) {
 return ResponseEntity.ok(userService.delete(id));
 }
}

Conclusion

I hope that in this part I have made Java development in the SAP Cloud Platform appealing to you. As you have hopefully realized, this is not witchcraft and not space science. I would like to give you one more tip – already during the planning of the project, make sure that everything is clearly structured and that you define your own package for entity, DTO, repository, service and controller.

Git Tutorial | Git Anleitung | Git Wissen | Git erklären Teil 3

Git Tutorial | Git Anleitung | Git Wissen | Git erklären Teil 3

Git Repositorys / Branches | Konflikte lösen

Dieses Git Tutorial macht dich zum Git Profi

Lernen Sie mit diesem Git Tutorial wie Git Repositories angelegt und in der SAP WebIDE verwendet werden können. Wir erklären Ihnen außerdem wie Konflikte gelöst werden können.

In der Blogreihe werden wir uns mit dem Thema Git beschäftigen und damit, warum das Arbeiten mit Git oder mit einem ähnlichen Tool für Entwickler fast schon unumgänglich ist.  Möchten Sie mehr über Git wissen oder haben Sie noch mehr Fragen zum Git Tutorial, nehmen Sie gerne Kontakt auf.

Die folgenden Informationen werden auch, jedoch um einiges ausführlicher, in unseren offiziell bei der SAP im Schulungskatalog gelisteten Kursen HOUI5 und HOFIObehandelt.

Besuchen Sie unsere SAP Standard Trainings

Wir haben mit dem HOUI5 und dem HOFIO zwei 5-Tage-Trainings auf höchstem Niveau erstellt! Lernen Sie von uns die professionelle SAPUI5 und SAP Fiori Entwicklung!

Inhalt des Git Tutorial

Auf folgende Themen werden wir in unserer Blogreihe eingehen:

  • Einführung in die Welt von Git
  • Begrifflichkeiten und die wichtigsten Funktionen
  • SAPUI5-Entwicklung mit Hilfe von Git
    • Exkurs: LDAP-Anbindung und Git-Server-Anbindung an die SAP Cloud Platform

Zielsetzung

Unser Ziel ist es, dass man nach unserem Git Tutorial die unten stehende Grafik interpretieren und mit Git die SAPUI5-Entwicklung optimieren kann.

Wichtig

Seitdem GitHub die Namenskonventionen umgestellt hat, heißt der Strang, der automatisch angelegt wird, nicht mehr „master“ bzw. „origin/master“, sondern „main“ und „origin/main“. Da die SAP WebIDE diese Änderung noch nicht erkennt, muss man das entweder in der WebIDE mitgeben oder in GitHub übersteuern.

Part 3 – Inhaltsverzeichnis Git Tutorial

  1. Repository anlegen (GitHub und SCP)
  2. Git Pane in der WebIDE + die wichtigsten Funktionen
  3. Conflict Handling

Über den Autor

Daniel Krancz

Daniel Krancz

SAP-Consultant / Software-Developer

Ich bin SAP-Berater und -Entwickler im SAPUI5/Fiori- und OData-Umfeld. Seit 2019 bin ich offiziell als externer Trainer bei SAP gelistet und halte Kurse (UX, S4, …) über SAP-Webentwicklungen und Cloud-Implementierungen im In- und Ausland. Seit 2021 bin ich SAP Press Autor beim Rheinwerk Verlag im Bereich SAP Mobile.

Einen letzten Punkt unseres Git Tutorial haben wir noch offen, nämlich die Verwendung von Git bei SAPUI5-Entwicklungen.

SAPUI5-Entwicklung – Git Tutorial

Ein jeder SAPUI5-Entwickler wird wahrscheinlich diesen Hinweis beim Importieren von bereits bestehenden SAPUI5-Apps aus dem ABAP-Repository schon mal gesehen haben:

Auch wenn man sonst noch nie was von Git gehört hat, wird man spätestens an dieser Stelle merken, dass es fast unabdingbar mit Git die Sourcen zu verwalten.

Aber wenn es um Git geht, stellt die SAP nicht nur diesen Hinweis zur Verfügung, sondern auch einen in die WebIDE integrierten Git Pane zur Verfügung.

Wie lege ich ein Git Repository an?

Für unser Beispiel werden wir zwei Git Repositories anlegen, nämlich einmal in GitHub und in der SAP Cloud Platform. Unser Beispielprojekt ist nachher nur mit dem GitHub Repository verknüpft. Im Lizenzmodell für die WebIDE sind auch die Git Repositories in der SAP Cloud Platform inkludiert.

GitHub

In GitHub können wir private Repositories anlegen, aber auch Repositories in Organisationen, in denen ich eingeladen worden bin.

Man hat zurzeit zwei Varianten an Repositories:

Public Repositories

  • für alle zugänglich, die den Link zur Repository haben
  • jeder kann clonen
  • Schreibrechte kann man einschränken (Push-Requests)

Private Repositories

  • nur für Sie oder für die Organisation sichtbar (siehe Owner)
  • Innerhalb Ihrer Org. kann man eigene Teams verwalten, die je nach Rolle Lese- und Schreibrechte haben

Zusätzlich sollte man noch einen README-File anlegen lassen, welche als Startseite für den Repository dient und dem User grundlegende Informationen vom Repo. anzeigen soll.

Es besteht auch die Möglichkeit, bestimmte Files und Filetypen ignorieren und bekannte Lizenzmodelle hinzufügen zu lassen. (.gitignore)

Nach dem Klick auf „Create repository“ wird man auch gleich in den Repository Browser geleitet:

Auf alle einzelnen Möglichkeiten möchte ich hier nicht detailliert eingehen, weil diese Informationen eh schon überall im Internet zu finden sind.

Kurz zusammengefasst:

  • man kann Lese- und Schreibrechte an bestimmte Personen und Teams vergeben
  • Commits und Pull-Requests verfolgen
  • Auswertungen ansehen
  • in den Einstellungen von Security bis Benachrichtigungen alles einstellen

Was für uns in Zukunft jetzt wichtig ist, ist der Repository URL. Bei einem Clone müssen wir diese URL angeben, denn diese ist der Zeiger zu unserem Repository:

https://github.com/clouddnagmbh/blogrepo

SAP Cloud Platform

Im Subaccount in der SCP (SAP Cloud Platform) hat man links im Menü die Möglichkeit Repositorys zu verwalten. Wenn Sie diesen Menüpunkt nicht sehen, dann fehlen Ihnen wahrscheinlich die nötigen Berechtigungen.

Mit dem „New Repository“-Button können wir gleich einen Repository anlegen. Hier ist natürlich wichtig zu wissen, dass die Authentifizierung über die SCP erfolgt.

Nachdem wir einen Namen und Beschreibung vergeben haben, können wir noch bestimmen, dass unser Repository mit einem initialen leeren Commit angelegt werden soll.

Wenn wir die Details zu unserer Repository anschauen, dann können wir ein paar Informationen ablesen und direkt auch einige Funktionen ausführen.

Es besteht die Möglichkeit, wieder den Namen zu editieren, unserem Repository nur Leserechte zu geben, den Garbage Collector zu starten, aber auch unser Repository direkt in die WebIDE clonen zu lassen.

Zwei wichtige Links sieht man auch gleich, nämlich einen mit dem Zeiger auf unseren Repository (wichtig, wenn man clonen möchte) und einen für den Repository Browser (nichts anderes als ein Explorer).

Was für uns in Zukunft jetzt wichtig ist, ist der Repository URL. Bei einem Clone müssen wir diese URL angeben, denn diese ist der Zeiger zu unserem Repository:

https://git.eu2.hana.ondemand.com/<subaccount>/blogrepo

Entwicklung- Git Tutorial

In unserer WebIDE können wir an mehreren Stellen einen Git Repository clonen. Anbei eine Möglichkeit:

Alle Varianten würden im Endeffekt zu diesem Wizard führen:

Hier müssen wir unsere Repository URL einfügen. Zusätzlich könnte man eine Konfiguration für Gerrit, ein Review-System von Google, hinzufügen.

Nach einer erfolgreichen Authentifizierung und Clone sehen wir die geclonte Applikation in unserem Workspace:

Jedoch bringt uns das in unserem Fall noch nichts, weil unser Repository leer ist. Wir haben ja im zweiten Teil unserer Blogreihe gelernt, dass ein Entwickler der Erste sein muss, der quasi eine Rahmenapplikation deployed.

Also lösche ich vorerst das geclonte Projekt, lege eine neue SAPUI5-App an und mit Rechtsklick auf das Projekt lege ich ein neues lokales Git Repository an:

In einem Popup kann man den Benutzernamen und die Mailadresse hinterlegen, welche bei den Commits als Author eingetragen wird:

Der Hinweis, dass das nicht mehr geändert werden kann, stimmt hier nicht ganz. Über Project -> Project Settings -> Git Repository kann das ganze geändert werden.

Nach dem ich das lokale Git Repository angelegt habe, bekomme ich auch gleich die Information, dass ich einen Remote Repository anbinden soll:

Das machen wir auch gleich und tragen unseren bereits erstellen Remote Repository ein:

Da das Ganze im Grunde genommen nichts anderes ist, als das Zusammenführen von Local und Remote Repository, wird zuerst noch ein Fetch gemacht.

Wir öffnen direkt den integrierten Git Pane:

Nun können wir hier in einer grafischen Oberfläche verschiedene Funktionen ausführen:

  1. Wie wir im zweiten Teil unserer Blogreihe kennengelernt haben, könnte man hier auf die verschiedensten Funktionen (Fetch, Merge, Pull, …) zugreifen.
  2. Man sieht auf einem Blick um welchen Local Repository es sich handelt und man könnte neue Local Branches erstellen und zwischen diesen wechseln.
  3. Eine Liste der Änderungen seit dem letzten Commit

Nocht nicht genug ? Weiter gehts ….

4. Alle Änderungen, die sich im Staging Area befinden, werden auch beim nächsten Commit mitgenommen. Also wenn ich Änderungen habe, die ich noch nicht veröffentlichen will, dann werde ich diese aus der Staging Area rausnehmen und somit beim nächsten Commit nicht mit den anderen teilen.

5. Jeder Commit erfordert eine Beschreibung, wie zum Beispiel eine kurze Zusammenfassung meiner Änderungen.

6. Die Möglichkeit für Commit und Push findet man ganz unten.

Symbole in der WebIDE

Initial Commit and Push

Wir haben einen Rahmenprojekt angelegt und sind der erste, der das Projekt auf den Remote Repository pusht:

Nach einem erfolgreichen Push-Request können wir die Sourcen am Repository Browser ansehen:

Nehmen wir jetzt an, dass wir diese Applikation nicht erstellt haben. Dann müssten wir genau an dieser Stelle einen Clone in unsere WebIDE vollziehen:

Bei einem Clone erhält unser Projekt in der WebIDE den Namen vom Remote Repository. Gleich daneben steht auch, auf welchem lokalen Branch wir gerade arbeiten:

Wie erstelle ich einen Local Branch im Git?

Im Git Pane haben wir die Möglichkeit zu unserem standardmäßig erstellen „master“ Branch weitere Local Branches zu erstellen.

Man wählt die Quelle aus (kann auch ein Remote Branch sein) und benennt den zu erstellenden lokalen Branch (in unserem Fall „featureA“):

Nach dem Erstellen befinden wir uns auch schon direkt in dem Feature Branch. Sie können mit Hilfe des Drop-Downs auch wieder wechseln.

In unserem Feature Branch machen wir ein paar Änderungen, legen neue Views an und fügen ein Label in die View ein. Wir beschreiben die Änderungen dementsprechend und committen sie:

Nachdem wir mit unseren Feature Implementierungen fertig sind, möchten wir diese mit anderen Entwicklern teilen. Wir haben in unserem Git Pane einen Indikator, dass wir seit dem letzten Merge einen Commit gemacht haben:

Nun wechseln wir in den lokalen „master“ Branch und machen einen Merge mit unserem Feature Branch. Wir verbinden die zwei Datenstände miteinander und prüfen auf eventuelle Konflikte:

Nachdem der Merge (in unserem Fall) ohne Konflikte durchgelaufen ist, checken wir die Sourcen im „master“ Branch und sehen, dass die Änderungen aus unserem Feature Branch in unserem lokalen „master“ Branch gelandet sind:

Wir hätten unseren Feature Branch natürlich auch gleich mit dem Remote Branch zusammenführen können, aber so konnten wir schonmal sichergehen, dass es mit dem letzten „origin/master“ Branch, welche ja unserem lokalen „master“ Branch entsprochen hat, keine Konflikte gibt.

Das bringt den anderen Entwicklern jetzt noch nichts. Nun müssen wir diese Änderungen auch auf den Remote Repository pushen. Aber wie wir schon im zweiten Teil unserer Blogreihe gelernt haben, müssen wir vorher checken, ob die anderen auch schon Änderungen gemacht und am Remote Repository gepusht haben.

Ich bevorzuge an dieser Stelle einen Fetch. In der Zusammenfassung schaue ich mir auch an, ob und was andere Entwickler gemacht haben:

Wir sehen, dass sich am Remote Repository seit unserem letzten Fetch nichts getan hat, somit können wir beruhigt pushen:

Wenn wir jetzt in den Browser Repository wechseln, dann werden wir auch sehen, dass dort unsere Änderungen bzw. unsere Feature Entwicklung sichtbar ist:

Was man hier noch gut sieht ist, dass nur die Deltas ausgetauscht worden sind. Die nicht geänderten Files haben noch immer den letzten Status und auch die letzte Commit Beschreibung, weil wir selbst die anderen Files, auch wenn wir am Projekt was geändert haben, nicht manipuliert haben.

Wie funktioniert das Conflict Handling mit Git?

In unserem Fall war das jetzt ganz einfach, da kein anderer Entwickler uns in die Quere kommen konnte. Aber wie ein Konflikt aussehen würde, haben wir hier für Sie simuliert.

Ich habe mich umentschieden und in meiner WebIDE im Label doch einen anderen Text eingetragen:

Diese Änderung werde ich jetzt wie gewohnt in die Staging Area bringen und mit einer entsprechenden Beschreibung einen Commit durchführen:

Wir machen einen Fetch um zu sehen, ob andere Entwickler auch was gemacht haben:

Da sehen wir jetzt, dass ein anderer Entwickler auch etwas geändert hat. Vorerst kein Problem, das ist ja eine ganz normale Situation, da unser Entwicklungsteam groß ist.

Was für uns aber neu ist, ist dieser Indikator:

Das bedeutet wir haben einen Commit seit dem letzten Merge gemacht und auch am dazugehörigen Remote Repo. hat es einen Commit seither gegeben.

Wir machen jetzt einen Merge, um die zwei Datenstände miteinander zu vereinen und da bemerken wir, dass es Konflikte gibt:

Ein Klick auf „Resolve Conflicts“ und dann landen wir es in einen grafischen Editor für das Beheben der Konflikte:

Hier sieht man die zwei Versionen einander gegenübergestellt und auch die Version, die nach unserer Konfliktbehebung entstehen würde.

Mit den Buttons rechts oben könnten wir auf die Schnelle sagen, ob wir unsere Änderungen oder die Änderungen vom Remote Repo. nehmen möchten. Hinter diesem grafischen Editor steht eigentlich nichts anderes als ein Code mit bestimmten Abschnitten und Markierungen, wo Konflikte aufgetreten sind:

Also könnte man im grafischen Editor die Konflikte lösen, aber auch im Code. Man würde hier genau diesen Abschnitt rausschmeißen, welchen man als veraltet empfindet.

Wenn wir alle Konflikte behoben haben, dann bleibt uns nichts anderes mehr übrig, als einen Commit zu machen, je nachdem wie viel Zeit wir für die Konflikten aufgewendet haben, wieder einen Fetch mit Merge zu machen und anschließend unseren Code zu pushen.

Bei einem Conflict Handling haben wir während dem Beheben auch nicht alle Funktionen zur Verfügung:

Wir können lediglich einen Fetch machen und uns den aktuellen Stand holen oder die bereits gemachten Änderungen zurücksetzen.

Nach einem Commit von unserer Fehlerbehebung sehen wir am Indikator, dass wir nun nicht einen Commit vor und auch gleichzeitig nach dem origin/master sind, sondern zwei Commits voraus. Einmal haben wir ja unser Label geändert und einmal einen Merge mit Konfliktbehebung (= erneuter Commit) gemacht.

Zusammenfassung – Git Tutorial

Jetzt steht uns nichts mehr im Wege! Wir haben gelernt, wie wir unsere Entwicklungen mit Git optimieren können und in diesem Bereich vor allem das Zusammenarbeiten mit anderen Entwicklern. Der nächste und letzter Teil unserer Blogreihe, nämlich ein Exkurs von Martin Koch verfasst und präsentiert, wird demnächst veröffentlicht.

Git und SAPUi5