[JAVA] Ich habe Spring Data JDBC 1.0.0.BUILD-SNAPSHOT ausprobiert (-> 1.0.0.RELEASE)

Der erste Eintrag im Jahr 2018 ist ... Ich habe @ sndrs "Spring Data JDBC Preview" gesehen und dachte "Hey" Spring Data JDBC Es ist ein Memo, als ich es ausprobiert habe. Es scheint immer noch eine einfache Unterstützung auf CRUD-Ebene zu sein, aber mit der offiziellen Veröffentlichung von Spring Data JDBC (https://github.com/spring-projects/spring-data-jdbc), Spring Data REST (https) Wir freuen uns (sehr) darauf, unterstützt zu werden unter: //projects.spring.io/spring-data-rest/)! ↓ ** Seit der offiziellen Veröffentlichung am 21.09.2008 wurde der Inhalt basierend auf 1.0.0.RELEASE geändert! ** ** **

Überprüfungsversion

HINWEIS: Verlauf aktualisieren

2018-02-06 :

  • DATAJDBC-161 Unterstützt Änderungen der Benutzeroberfläche aufgrund der Unterstützung
  • Mit Spring Boot 2.0.0.RC1 erneut validiert

2018-02-08 :

  • Verwendung der Methode "@ Query" hinzugefügt (entspricht DATAJDBC-172)

2018-03-08 :

  • Es wurde hinzugefügt, dass der einfache Typ aufgrund der Unterstützung von DATAJDBC-175 als Rückgabewert der Methode @ @ Query unterstützt wird.
  • Mit Spring Boot 2.0.0.RELEASE erneut validiert

2018-03-09 :

  • Beschreibung zum Umgang mit verwandten Objekten hinzugefügt
  • In Bezug auf das Obige wurde hinzugefügt, dass es notwendig ist, Lovelace-BUILD-SNAPSHOT des Spring-Data-Release-Zugs zu importieren, wenn Spring Data JDBC auf Spring Boot 2.0.0.RELEASE verwendet wird.

2018-03-10 :

  • Verwendung der Update-Abfrage hinzugefügt (@ Modifying) (entspricht DATAJDBC-182)

2018-03-23 :

  • Behoben, dass "DefaultNamingStrategy" nicht verwendet wird, da DATAJDBC-189 unterstützt wird (Standardimplementierung ist als "NamingStrategy.INSTANCE" definiert)
  • Auf MyBatis Spring Boot Starter 1.3.2 aktualisiert

2018-03-31 :

  • Es wurde behoben, dass "NamedParameterJdbcOperations" beim Generieren von "DefaultDataAccessStrategy" nicht angegeben wurde, da DATAJDBC-155 unterstützt wurde.
  • NamingStrategy hinzugefügt, um Schlangenfälle (Unterstriche) mit Unterstützung von DATAJDBC-184 zu unterstützen.

2018-04-03 :

  • Mit Unterstützung für DATAJDBC-178 kann durch Festlegen einer beliebigen NamespaceStrategy-Instanz in MyBatisDataAccessStrategy die Namenskonvention des Namespace geändert werden. Es wurde hinzugefügt, dass es geändert werden kann

2018-05-18 :

  • Beschreibung zur Unterstützung der annotationsbasierten Überwachungsfunktion hinzugefügt (entsprechend DATAJDBC-204)
  • Update auf Spring Boot 2.0.2.RELEASE und erneutes Validieren

2018-05-19 :

  • Reflektierte Änderungen der Paketkonfiguration (entsprechend DATAJDBC-138)

2018-06-28 :

  • Reflektiert Änderungen der Paketkonfiguration und des Klassennamens (entspricht DATAJDBC-226)
  • Es wurde hinzugefügt, dass die Standardimplementierung von "NamingStrategy" als Reaktion auf DATAJDBC-207 in "Snake Case" geändert wurde.
  • Auf Spring Boot 2.0.3.RELEASE aktualisiert und erneut validiert

2018-07-03 :

  • Der explizite Import von Spring-Data-Releasetrain Lovelace-BUILD-SNAPSHOT wurde entfernt und <spring-data-releasetrain.version> Lovelace-BUILD-SNAPSHOT </ spring-data-releasetrain.version> zu den Eigenschaften hinzugefügt. Behoben, um hinzuzufügen

2018-07-20 :

  • Reflektierte die Änderung in der Anwendungsmethode des benutzerdefinierten Konverters (entsprechend DATAJDBC-235)
  • Es wurde ein Problem behoben, bei dem MyBatis beim Löschen von All einen Fehler ausgibt. Es scheint, dass sich die SQL-ID, die die Entität löscht, geändert hat! ??

2018-07-28 :

  • Reflektierte die Änderung in der Anwendungsmethode von JdbcConfiguration (entsprechend DATAJDBC-243)

2018-09-22 :

  • Die Version der Spring Data JDBC-Überprüfung wurde auf 1.0.0.RELEASE korrigiert
  • Die Spring Boot-Überprüfungsversion wurde auf 2.0.5.RELEASE korrigiert
  • Beschreibt Spring-Boot-Starter-Data-JDBC

2018-09-23 :

  • Die Erklärung zum Angeben / Erweitern von JdbcConfiguration wurde korrigiert (Korrektur aufgrund des Einflusses von DATA JDBC-267).

Demo-Projekt

Die in diesem Eintrag beschriebenen Quellen werden in den folgenden Repositories veröffentlicht. (Da Spring JDBC und MyBatis gemischt und verifiziert sind, gibt es einige Unterschiede zur Beschreibung im Eintrag.)

Entwicklungsprojekt erstellen

Wählen Sie zunächst in SPRING INITIALIZR "H2", "JDBC" und "MyBatis (nur bei Verwendung von MyBatis)" als Abhängigkeiten zum Erstellen eines Projekts aus. (Dieser Eintrag basiert auf dem Maven-Projekt) Fügen Sie als Nächstes "spring-data-jdbc" zu dem von SPRING INITIALIZR erstellten Projekt hinzu. Bei Verwendung von Spring Data JDBC in der Spring Boot 2.0-Serie muss die Lovelace-RELEASE-Version von Spring-Data-Releasetrain in den von Spring Boot bereitgestellten Eigenschaften wie folgt angegeben werden.

pom.xml


<properties>
	<spring-data-releasetrain.version>Lovelace-RELEASE</spring-data-releasetrain.version>
</properties>

pom.xml


<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-jdbc</artifactId>
</dependency>

DB-Setup

Bereiten Sie eine DDL vor, um eine Tabelle zu erstellen.

src/main/resources/schema.sql


CREATE TABLE IF NOT EXISTS todo (
	id IDENTITY
	,title TEXT NOT NULL
	,details TEXT
	,finished BOOLEAN NOT NULL
);

Erstellen eines Domänenobjekts

Erstellen Sie ein Todo-Objekt, das den Datensatz in der TODO-Tabelle darstellt. Fügen Sie der Eigenschaft, die den Schlüsselwert enthält, "@ Id" hinzu.

src/main/java/com/example/demo/domain/Todo.java


package com.example.demo.domain;

import org.springframework.data.annotation.Id;

public class Todo {
	@Id
	private int id;
	private String title;
	private String details;
	private boolean finished;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getDetails() {
		return details;
	}

	public void setDetails(String details) {
		this.details = details;
	}

	public boolean isFinished() {
		return finished;
	}

	public void setFinished(boolean finished) {
		this.finished = finished;
	}

}

Repository erstellen

Erstellen Sie eine Repository-Schnittstelle zum Bearbeiten von Domänenobjekten. Es geht darum, das von Spring Data bereitgestellte "Croud Repository" zu erben.

src/main/java/com/example/demo/repository/TodoRepository.java


package com.example.demo.repository;

import org.springframework.data.repository.CrudRepository;

import com.example.demo.domain.Todo;

public interface TodoRepository extends CrudRepository<Todo, Integer> {

}

Auf diese Weise ... Sie können das Todo-Objekt mit den folgenden in "CrudRepository" definierten Methoden bedienen.

Referenz: Auszug aus dem Crud Repository


package org.springframework.data.repository;

import java.util.Optional;

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
	<S extends T> S save(S entity);
	<S extends T> Iterable<S> saveAll(Iterable<S> entities);
	Optional<T> findById(ID id);
	boolean existsById(ID id);
	Iterable<T> findAll();
	Iterable<T> findAllById(Iterable<ID> ids);
	long count();
	void deleteById(ID id);
	void delete(T entity);
	void deleteAll(Iterable<? extends T> entities);
	void deleteAll();
}

So führen Sie SQL aus

Spring Data JDBC bietet "DataAccessStrategy" als Schnittstelle zum Abstrahieren der SQL-Ausführungsmethode. Derzeit sind die Implementierung von "Spring JDBC (" NamedParameterJdbcOperations ")" und "MyBatis" implementiert.

Verwenden der Spring JDBC-Implementierung

Wenn Sie die Spring JDBC-Implementierung verwenden, wird die SQL, die ausgeführt werden soll, wenn Sie die in "CrudRepository" definierte Methode aufrufen, automatisch generiert (= Sie müssen kein SQL für CRUD-Operationen schreiben).

Beispiel für eine Bohnendefinition

Erstellen Sie eine Konfigurationsklasse mit "@ EnableJdbcRepositories" und "@Import (JdbcConfiguration.class)". Jedoch ... Wenn Sie die in JdbcConfiguration definierte Bean ändern möchten, müssen Sie eine Konfigurationsklasse erstellen, die JdbcConfiguration erbt, und sie als DI-Container registrieren. Wenn Sie beispielsweise Typkonvertierungen benötigen, die standardmäßig nicht unterstützt werden, können Sie die Methode "jdbcCustomConversions" überschreiben und "JdbcCustomConversions" mit einem beliebigen "Konverter" zurückgeben. In diesem Eintrag wird "Konverter" hinzugefügt, um den TEXT-Typ ("Clob") der H2-Datenbank in "String" zu konvertieren. Übrigens ... Wenn Sie VARCHAR anstelle von TEXT verwenden, müssen Sie die Methode "jdbcCustomConversions" nicht überschreiben.

@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig extends JdbcConfiguration {

	@Override
	protected JdbcCustomConversions jdbcCustomConversions() { 
		return new JdbcCustomConversions(Collections.singletonList(new Converter<Clob, String>() {
			@Override
			public String convert(Clob clob) {
				try {
					return clob == null ? null : clob.getSubString(1L, (int) clob.length());
				} catch (SQLException e) {
					throw new IllegalStateException(e);
				}
			}
		}));
	}

}

Note:

NamingStrategy wird als Schnittstelle zur Bestimmung der Namensstrategie für Spalten- und Eigenschaftsnamen bereitgestellt. Standardmäßig wird "NamingStrategy.INSTANCE" verwendet. Sie können das Standardverhalten jedoch ändern, indem Sie eine Bean für "NamingStrategy" definieren. ~~ Zusätzlich wurde NamingStrategy, das Schlangenfälle (Unterstriche) unterstützt, durch die Unterstützung von DATAJDBC-184 (bei Verwendung) hinzugefügt. Bean Definition erforderlich). ~~ Das Standardverhalten wird als Schlangenfall behandelt (Unterstrich) (das Standardverhalten wurde geändert, um [DATAJDBC-206] zu unterstützen (https://jira.spring.io/browse/DATAJDBC-206)).

Verwenden der MyBatis-Implementierung

Bei Verwendung der MyBatis-Implementierung muss die auszuführende SQL definiert werden, wenn die in "CrudRepository" definierte Methode auf der MyBatis-Seite aufgerufen wird. (= Es ist notwendig, SQL auch für CRUD-Operationen zu schreiben).

Beispiel für eine Bohnendefinition

Erstellen Sie eine Konfigurationsklasse mit "@ EnableJdbcRepositories" und "@Import (JdbcConfiguration.class)" und definieren Sie eine Bean von "MyBatisDataAccessStrategy" (MyBatis-Implementierung) als "DataAccessStrategy".

@EnableJdbcRepositories
@Import(JdbcConfiguration.class)
@Configuration
public class SpringDataJdbcConfig {
	@Bean
	DataAccessStrategy dataAccessStrategy(SqlSession sqlSession) {
		return new MyBatisDataAccessStrategy(sqlSession);
	}
}

NOTE:

2018-02-06: Mit Unterstützung von DATAJDBC-161 wird das an das Konstruktorargument von "MyBatisDataAccessStrategy" zu übergebende Objekt von "SqlSessionFactory" an "SqlSession" übergeben. (Eigentlich geändert in SqlSessionTemplate).

Beispiel für eine MyBatis-Einstellung

Legen Sie den Speicherort und den Typalias der Mapper-XML-Datei fest.

src/main/resources/application.properties


mybatis.mapper-locations=classpath:/com/example/demo/mapper/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.domain

Beispiel für eine SQL-Definition

Definieren Sie die SQL entsprechend der Methode von "CrudRepository". Wenn Sie MyBatis über Spring Data JDBC verwenden, müssen Sie beim Definieren von SQL einige spezielle Regeln beachten.

NOTE:

2018-04-03: Mit Unterstützung von DATAJDBC-178 durch Festlegen einer beliebigen NamespaceStrategy-Instanz in MyBatisDataAccessStrategy, dem Namen Sie können die Namenskonvention für Leerzeichen ändern.

src/main/resources/com/example/demo/mapper/TodoMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.TodoMapper">

	<!-- statements for CrudRepository method -->
	<insert id="insert" useGeneratedKeys="true" keyProperty="instance.id">
		INSERT INTO todo 
			(title, details, finished)
		VALUES 
			(#{instance.title}, #{instance.details}, #{instance.finished})
	</insert>
	<update id="update">
		UPDATE todo SET
			title = #{instance.title}, details = #{instance.details}, finished = #{instance.finished}
		WHERE
			id = #{instance.id}
	</update>
	<delete id="delete">
		DELETE FROM todo WHERE id = #{id}
	</delete>
	<delete id="deleteAll">
		DELETE FROM todo
	</delete>
	<select id="existsById" resultType="_boolean">
		SELECT count(*) FROM todo WHERE id = #{id}
	</select>
	<select id="findById" resultType="Todo">
		SELECT
			id, title, details, finished 
		FROM
			todo
		WHERE
			id = #{id}
	</select>
	<select id="findAll" resultType="Todo">
		SELECT
			id, title, details, finished
		FROM
			todo
		ORDER BY
			id
	</select>
	<select id="findAllById" resultType="Todo">
		SELECT
			id, title, details, finished
		FROM
			todo
		<where>
			<foreach collection="id" item="idValue" open="id in("
				separator="," close=")">
				#{idValue}
			</foreach>
		</where>
		ORDER BY
			id
	</select>
	<select id="count" resultType="_long">
		SELECT count(*) FROM todo
	</select>

</mapper>

Referenz: Auszug aus MyBatisContext


package org.springframework.data.jdbc.mybatis;

import java.util.Map;

public class MyBatisContext {

	private final Object id;
	private final Object instance;
	private final Class domainType;
	private final Map<String, Object> additonalValues;

	public MyBatisContext(Object id, Object instance, Class domainType, Map<String, Object> additonalValues) {
		this.id = id;
		this.instance = instance;
		this.domainType = domainType;
		this.additonalValues = additonalValues;
	}

	public Object getId() {
		return id;
	}

	public Object getInstance() {
		return instance;
	}

	public Class getDomainType() {
		return domainType;
	}

	public Object get(String key) {
		return additonalValues.get(key);
	}
}

Kombinierte Implementierung

Ich werde es in diesem Eintrag nicht erklären + Ich habe es nicht überprüft, aber es scheint möglich zu sein, mehrere Implementierungen (z. B. Spring JDBC und MyBatis) zusammen mit CascadingDataAccessStrategy zu verwenden.

Verwendung des Repositorys

Das Spring Data JDBC-Repository wird wie jedes andere Spring Data-Projekt injiziert und verwendet.

@Autowired
private TodoRepository todoRepository;

@Test
public void insertAndFineById() {
	Todo newTodo = new Todo();
	newTodo.setTitle("Trinkparty");
	newTodo.setDetails("Ginza 19:00");
	todoRepository.save(newTodo);

	Optional<Todo> todo = todoRepository.findById(newTodo.getId());
	Assertions.assertThat(todo.isPresent()).isTrue();
	Assertions.assertThat(todo.get().getId()).isEqualTo(newTodo.getId());
	Assertions.assertThat(todo.get().getTitle()).isEqualTo(newTodo.getTitle());
	Assertions.assertThat(todo.get().getDetails()).isEqualTo(newTodo.getDetails());
	Assertions.assertThat(todo.get().isFinished()).isFalse();
}

Hinzufügen der Methode @ @ Query`

Jede Abfrage kann (mithilfe der Spring JDBC-Funktion) ausgeführt werden, indem dem Repository eine Methode mit "@ Query" hinzugefügt wird.

~~WARNING:~~ ~~ Mit der aktuellen Implementierung ist es nicht möglich, Update SQL auszuführen (es scheint Pläne zu geben, dies zu unterstützen). ~~

NOTE: 10.03.2018: DATAJDBC-182 unterstützt auch die Ausführung von Update SQL.

src/main/java/com/example/demo/repository/TodoRepository.java


package com.example.demo.repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import com.example.demo.domain.Todo;

public interface TodoRepository extends CrudRepository<Todo, Integer> {

	@Query("SELECT * FROM todo WHERE id = :id")
	Optional<Todo> findOptionalById(@Param("id") Integer id);

	@Query("SELECT * FROM todo WHERE id = :id")
	Todo findEntityById(@Param("id") Integer id);

	@Query("SELECT * FROM todo ORDER BY id")
	Stream<Todo> findAllStream();

	@Query("SELECT * FROM todo ORDER BY id")
	List<Todo> findAllList();

	@Query("SELECT count(*) FROM todo WHERE finished = :finished")
	long countByFinished(@Param("finished") Boolean finished);

	@Query("SELECT count(*) FROM todo WHERE finished = :finished")
	boolean existsByFinished(@Param("finished") Boolean finished);

	@Query("SELECT current_timestamp()")
	LocalDateTime currentDateTime();

	@Modifying
	@Query("UPDATE todo SET finished = :finished WHERE id = :id")
	boolean updateFinishedById(@Param("id") Integer id, @Param("finished") boolean finished);

}

NOTE:

@ Param kann durch Angabe der Option -parameters des Java-Compilers weggelassen werden.

Derzeit werden folgende Rückgabetypen unterstützt

Und "org.springframework.data.domain.Page " und "org.springframework.data.domain.Slice " werden nicht unterstützt (wenn Sie diese Typen als Rückgabewerte behandeln möchten). "Benutzerdefinierte Operation hinzufügen", die später beschrieben wird, ist erforderlich.

Darüber hinaus wird als Rückgabewert der Aktualisierungsmethode Folgendes unterstützt

Ist.

~~WARNING:~~ ~~ In der aktuellen Implementierung ist es nicht möglich, einen anderen Typ als die Domänenklasse anzugeben, z. B. einen numerischen Wert (int, long usw.) oder einen booleschen Wert als Rückgabewert (dh ... @ Query -Methode-Ausbuchung). Sie können kein SQL angeben, um die Nummer abzurufen, oder ein SQL, um das Vorhandensein von Datensätzen zu überprüfen. Es kann mit der Methode behandelt werden, die unten unter "Hinzufügen von benutzerdefinierten Operationen" vorgestellt wird, aber ... Ich habe das Gefühl, dass mir einige Überlegungen fehlen, daher werde ich ein Problem damit behandeln. ⇒ DATAJDBC-175 ~~ ↓ 2018-03-08: Es ist jetzt möglich, andere Typen als Domänenklassen (sogenannte einfache Typen) wie Zahlen (int, long usw.) und boolesche Werte als Rückgabewerte zurückzugeben! !! Intern ... SingleColumnRowMapper + Die Auflösung des Spring-Datentyps wird in Zusammenarbeit mit ConversionService durchgeführt, das auf JDBC angewendet wird. Übrigens ist für diese Unterstützung Spring Framework 5.0.4.RELEASE oder höher erforderlich.

Fügen Sie benutzerdefinierte Operationen hinzu

"Mechanismus zum Hinzufügen benutzerdefinierter Operationen (benutzerdefinierte Methoden)" in Spring Data Dieser Mechanismus kann auch in Spring Data JDBC verwendet werden.

Erstellen einer benutzerdefinierten Schnittstelle

Definieren Sie eine Schnittstelle zum Definieren benutzerdefinierter Operationen (benutzerdefinierte Methoden).

src/main/java/com/example/demo/repository/CustomizedTodoRepository.java


package com.example.demo.repository;

import com.example.demo.domain.Todo;

public interface CustomizedTodoRepository {

	Iterable<Todo> findAllByFinished(boolean finished);

}

Erben Sie die erstellte Schnittstelle mit "Todo Repository".

src/main/java/com/example/demo/repository/TodoRepository.java


package com.example.demo.repository;

import org.springframework.data.repository.CrudRepository;

import com.example.demo.domain.Todo;

public interface TodoRepository extends CrudRepository<Todo, Integer>, CustomizedTodoRepository {

}

Erstellen einer Spring JDBC-Implementierung

Erstellen Sie bei Verwendung von Spring JDBC die folgende Implementierungsklasse.

src/main/java/com/example/demo/repository/CustomizedTodoRepositoryImpl.java


package com.example.demo.repository;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;

import com.example.demo.domain.Todo;

public class CustomizedTodoRepositoryImpl implements CustomizedTodoRepository {

	private static final RowMapper<Todo> ROW_MAPPER = new BeanPropertyRowMapper<>(Todo.class);

	private final NamedParameterJdbcOperations namedParameterJdbcOperations;

	public CustomizedTodoRepositorySpringJdbcImpl(NamedParameterJdbcOperations namedParameterJdbcOperations) {
		this.namedParameterJdbcOperations = namedParameterJdbcOperations;
	}

	public Iterable<Todo> findAllByFinished(boolean finished) {
		return this.namedParameterJdbcOperations.query(
				"SELECT id, title, details, finished FROM todo WHERE finished = :finished ORDER BY id",
				new MapSqlParameterSource("finished", finished), ROW_MAPPER);
	}

}

Erstellen einer MyBatis-Implementierung

Erstellen Sie bei Verwendung von MyBatis die folgende Implementierungsklasse.

src/main/java/com/example/demo/repository/CustomizedTodoRepositoryImpl.java


package com.example.demo.repository;

import org.apache.ibatis.session.SqlSession;

import com.example.demo.domain.Todo;

public class CustomizedTodoRepositoryImpl implements CustomizedTodoRepository {

	private final String NAMESPACE = Todo.class.getName() + "Mapper";

	private final SqlSession sqlSession;

	public CustomizedTodoRepositoryMyBatisImpl(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	public Iterable<Todo> findAllByFinished(boolean finished) {
		return this.sqlSession.selectList(NAMESPACE + ".findAllByFinished", finished);
	}

}

Fügen Sie auch die SQL-Definition hinzu.

src/main/resources/com/example/demo/mapper/TodoMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.TodoMapper">

	<!-- ... -->

	<!-- statements for custom repository method -->
	<select id="findAllByFinished" resultType="Todo">
		SELECT
			id, title, details, finished
		FROM
			todo
		WHERE
			finished = #{finished}
		ORDER BY
			id
	</select>

</mapper>

Persistenzoperation eines verwandten Objekts (1: 1 oder 1: N)

Spring Data JDBC unterstützt Persistenzoperationen für verwandte Objekte mit einer 1: 1- oder 1: N-Beziehung. Der Supportstatus unterscheidet sich jedoch zwischen der Spring JDBC-Implementierung und der MyBatis-Implementierung. Wenn Sie einen kurzen Blick darauf werfen ... Der Support-Status für Update-Vorgänge ist der gleiche. Für den Betrieb des Referenzsystems bei Verwendung von MyBatis ist jedoch eine Implementierung auf der MyBatis-Seite (Tabellenverknüpfung + 1: 1/1: N-Zuordnung unter Verwendung von Zuordnung und Sammlung) erforderlich.

DB-Setup

Erstellen Sie eine Tabelle, um verwandte Objekte beizubehalten.

src/main/resources/schema.sql


CREATE TABLE IF NOT EXISTS activity (
	id IDENTITY
	,todo INTEGER NOT NULL --Spalte, in der die ID des Domänenobjekts gespeichert ist
	,todo_key INTEGER NOT NULL --Spalte, in der der Identifikationsschlüssel (und der Sortierschlüssel) verwandter Objekte im Domänenobjekt gespeichert sind
	,content TEXT NOT NULL
	,at TIMESTAMP NOT NULL
);

In der Standardimplementierung von Spring Data JDBC lautet der Spaltenname "Spalte, in der die ID des Domänenobjekts gespeichert ist" "Klassenname des Domänenobjekts" und der Spaltenname "Spalte, in der der Identifikationsschlüssel des zugehörigen Objekts im Domänenobjekt gespeichert ist" Es wird zu "Spalte, in der die ID des Domänenobjekts +" _key "" gespeichert ist.

Verwandte Objekte erstellen

Erstellen Sie ein Domänenobjekt, das die Aktivität von TODO darstellt, und ordnen Sie es dem Todo-Objekt zu.

src/main/java/com/example/demo/domain/Activity.java


package com.example.demo.domain;

import org.springframework.data.annotation.Id;

import java.time.LocalDateTime;

public class Activity {
	@Id
	private int id;
	private String content;
	private LocalDateTime at;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public LocalDateTime getAt() {
		return at;
	}

	public void setAt(LocalDateTime at) {
		this.at = at;
	}
}

src/main/java/com/example/demo/domain/Todo.java


public class Todo {
	// ...
	private List<Activity> activities;
	// ...
	public List<Activity> getActivities() {
		return activities;
	}
	public void setActivities(List<Activity> activities) {
		this.activities = activities;
	}
}

Beispiel für die Ausführung einer CRUD-Operation

Hier wird das Domänenobjekt, das das zugehörige Objekt mit einer 1: N-Beziehung enthält, unter Verwendung der in "CrudRepository" definierten Methode betrieben.

Beispiel für die Ausführung einer CRUD-Operation


@Test
public void oneToMany() {

	// Insert
	Todo newTodo = new Todo();
	newTodo.setTitle("Trinkparty");
	newTodo.setDetails("Ginza 19:00");
	Activity activity1 = new Activity();
	activity1.setContent("Created");
	activity1.setAt(LocalDateTime.now());

	Activity activity2 = new Activity();
	activity2.setContent("Started");
	activity2.setAt(LocalDateTime.now());

	newTodo.setActivities(Arrays.asList(activity1, activity2));

	todoRepository.save(newTodo);

	// Assert for inserting
	Optional<Todo> loadedTodo = todoRepository.findById(newTodo.getId());
	Assertions.assertThat(loadedTodo.isPresent()).isTrue();
	loadedTodo.ifPresent(todo -> {
		Assertions.assertThat(todo.getId()).isEqualTo(newTodo.getId());
		Assertions.assertThat(todo.getTitle()).isEqualTo(newTodo.getTitle());
		Assertions.assertThat(todo.getDetails()).isEqualTo(newTodo.getDetails());
		Assertions.assertThat(todo.isFinished()).isFalse();
		Assertions.assertThat(todo.getActivities()).hasSize(2);
		Assertions.assertThat(todo.getActivities().get(0).getContent()).isEqualTo(activity1.getContent());
		Assertions.assertThat(todo.getActivities().get(1).getContent()).isEqualTo(activity2.getContent());

	});

	// Update
	Activity activity3 = new Activity();
	activity3.setContent("Changed Title");
	activity3.setAt(LocalDateTime.now());
	loadedTodo.ifPresent(todo -> {
		todo.setTitle("[Change] " + todo.getTitle());
		todo.getActivities().add(activity3);
	});
	todoRepository.save(loadedTodo.get());

	// Assert for updating
	loadedTodo = todoRepository.findById(newTodo.getId());
	Assertions.assertThat(loadedTodo.isPresent()).isTrue();
	loadedTodo.ifPresent(todo -> {
		Assertions.assertThat(todo.getTitle()).isEqualTo("[Change] " + newTodo.getTitle());
		Assertions.assertThat(todo.getActivities()).hasSize(3);
		Assertions.assertThat(todo.getActivities().get(0).getContent()).isEqualTo(activity1.getContent());
		Assertions.assertThat(todo.getActivities().get(1).getContent()).isEqualTo(activity2.getContent());
		Assertions.assertThat(todo.getActivities().get(2).getContent()).isEqualTo(activity3.getContent());
	});

	// Delete
	todoRepository.deleteById(newTodo.getId());

	// Assert for deleting
	Assertions.assertThat(todoRepository.findById(newTodo.getId())).isNotPresent();

}

Verwenden der Spring JDBC-Implementierung

Wenn Sie die Spring JDBC-Implementierung verwenden, müssen Sie nichts tun. Wenn Sie einfach die CrudRepository-Methode aufrufen, generiert Spring Data JDBC SQL und führt es aus.

Verwenden der MyBatis-Implementierung

Wenn Sie die MyBatis-Implementierung verwenden, müssen Sie SQL in der Mapper-XML-Datei definieren.

Erstens ... Definieren Sie die SQL zum Einfügen verwandter Objekte.

src/main/resources/com/example/demo/mapper/ActivityMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
	<insert id="insert" useGeneratedKeys="true" keyProperty="instance.id">
		INSERT INTO activity
			(todo, todo_key, content, at)
		VALUES
			(#{additonalValues.Todo}, #{additonalValues.Todo_key}, #{instance.content}, #{instance.at})
	</insert>
</mapper>

Der Punkt hier ist, dass die "ID des Domänenobjekts" und der "Identifikationsschlüssel (und Sortierschlüssel) des zugehörigen Objekts im Domänenobjekt" in der Eigenschaft "additonalValues" vom Typ "Map" gespeichert sind. , Der Schlüsselname, in dem der Wert gespeichert ist, entspricht der Regel des Spaltennamens.

Weiter ... Definieren Sie SQL für DELETE-bezogene Objekte. Diese SQL wird auch beim Aktualisieren eines Domänenobjekts aufgerufen. Mit anderen Worten ... Alle verwandten Objekte mit einer 1: N-Beziehung werden GELÖSCHT und dann erneut EINFÜGEN.

src/main/resources/com/example/demo/mapper/TodoMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.TodoMapper">
	<!-- ... -->
	<delete id="delete-activities">
		DELETE FROM activity WHERE todo = #{id}
	</delete>
	<delete id="deleteAll-activities">
		DELETE FROM activity WHERE todo = #{id}
	</delete>
	<!-- ... -->
</mapper>

Schließlich ... Ändern, um verwandte Objekte in der SQL abzurufen, die auf das Domänenobjekt verweisen. Insbesondere ... Verbinden Sie die Tabellen, die die Informationen zu verwandten Objekten enthalten, und ordnen Sie die zugehörigen Objekte mithilfe der ResultMap-Funktion von MyBatis Domänenobjekten zu.

src/main/resources/com/example/demo/mapper/TodoMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
	<!-- ... -->
	<select id="findById" resultMap="todoMap">
		SELECT
			t.id, t.title, t.details, t.finished
			, a.id as activity_id, a.content as activity_content, a.at as activity_at
		FROM
			todo t
		LEFT OUTER JOIN activity a ON a.todo = t.id
		WHERE
			t.id = #{id}
		ORDER BY
			a.todo_key
	</select>
	<resultMap id="todoMap" type="Todo">
		<id column="id" property="id"/>
		<result column="title" property="title"/>
		<result column="details" property="details"/>
		<result column="finished" property="finished"/>
		<collection property="activities" columnPrefix="activity_" ofType="Activity">
			<id column="id" property="id"/>
			<result column="content" property="content"/>
			<result column="at" property="at"/>
		</collection>
	</resultMap>
	<!-- ... -->
</mapper>

Spalte für Zuordnungsspalte umbenennen

Wenn Ihnen die von der Standardimplementierung von Spring Data JDBC generierten Spaltennamen nicht gefallen ... Sie können sie ändern, indem Sie die Implementierungsklasse von "NamingStrategy" als Bean definieren. Hier werden wir vorstellen, wie man "Todo" in "todo_id" und "Todo_key" in den Spaltennamen "sort_order" ändert.

Zuerst ... Ändern Sie den Spaltennamen der Tabelle.

src/main/resources/schema.sql


CREATE TABLE IF NOT EXISTS activity (
	id IDENTITY
	,todo_id INTEGER NOT NULL --Spaltennamen ändern
	,sort_order INTEGER NOT NULL --Spaltennamen ändern
	,content TEXT NOT NULL
	,at TIMESTAMP NOT NULL
);

Weiter ... Bean-Definition der Implementierungsklasse von NamingStrategy.

@Bean
NamingStrategy namingStrategy() {
	return new NamingStrategy(){
		@Override
		public String getReverseColumnName(RelationalPersistentProperty property) {
			return NamingStrategy.super.getReverseColumnName(property).toLowerCase() + "_id";
		}
		@Override
		public String getKeyColumn(RelationalPersistentProperty property) {
			return "sort_order";
		}
	};
}

Wenn Sie die MyBatis-Implementierung verwenden, müssen Sie auch die SQL ändern.

src/main/resources/com/example/demo/mapper/ActivityMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
	<insert id="insert" useGeneratedKeys="true" keyProperty="instance.id">
		INSERT INTO activity
			(todo_id, sort_order, content, at)
		VALUES
			(#{additonalValues.todo_id}, #{additonalValues.sort_order}, #{instance.content}, #{instance.at})
	</insert>
</mapper>

src/main/resources/com/example/demo/mapper/TodoMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
	<!-- ... -->
	<delete id="delete-activities">
		DELETE FROM activity WHERE todo_id = #{id}
	</delete>
	<delete id="deleteAll-activities">
		DELETE FROM activity WHERE todo_id = #{id}
	</delete>
	<!-- ... -->
	<select id="findById" resultMap="todoMap">
		SELECT
			t.id, t.title, t.details, t.finished
			, a.id as activity_id, a.content as activity_content, a.at as activity_at
		FROM
			todo t
		LEFT OUTER JOIN activity a ON a.todo_id = t.id
		WHERE
			t.id = #{id}
		ORDER BY
			a.sort_order
	</select>
	<!-- ... -->
</mapper>

Festlegen von Werten für Überwachungsspalten (Überwachungsfunktion)

In Spring Data "Mechanismus zum Festlegen von Werten in der Spalte (Prüfspalte), die enthält, wann, wer und wann und aktualisierte (zuletzt aktualisierte) Daten -data / commons / docs / 2.1.0.RELEASE / reference / html / # auditing), und diese Funktion kann auch mit Spring Data JDBC verwendet werden.

IMPORTANT:

Zum Zeitpunkt von Spring Data JDBC 1.0.0.RELEASE (Lovelace) gibt es einen Teil, in dem die Überwachungsfunktion für verschachtelte Objekte (1: 1, 1: N) nicht unterstützt werden kann. Dies scheint auf der Seite von Spring Data Commons ein Problem zu sein, nicht auf der Seite von Spring Data JDBC.

  • [DATACMNS-1297] (https://jira.spring.io/projects/DATACMNS/issues/DATACMNS-1297): Ein Fehler tritt immer dann auf, wenn die Audit-Eigenschaft in der Klasse auf der N-Seite mit einer 1: N-Beziehung (Daten) vorhanden ist. Wird zu einem Fehler führen, auch wenn 0 Fälle vorliegen)
  • [DATACMNS-1296] (https://jira.spring.io/projects/DATACMNS/issues/DATACMNS-1296) : Ein Fehler tritt auf, wenn das Objekt selbst "null" ist, wenn die Eigenschaft für die Überwachung in der verschachtelten Klasse vorhanden ist.

Aktivieren Sie die Überwachungsfunktion

Um die Überwachungsfunktion zu verwenden, fügen Sie der Konfigurationsklasse "@ org.springframework.data.jdbc.repository.config.EnableJdbcAuditing" hinzu.

@EnableJdbcAuditing //hinzufügen
@Import(JdbcConfiguration.class)
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig {
    // ...
}

Eine Klasse, die die Schnittstelle org.springframework.data.domain.AuditorAware implementiert, wenn Sie aufzeichnen möchten, wer sie erstellt und aktualisiert hat.

package com.example.demo;

import org.springframework.data.domain.AuditorAware;

import java.util.Optional;

public class MyAuditorAware implements AuditorAware<String> {

	static ThreadLocal<String> currentUser = ThreadLocal.withInitial(() -> "default");

	public Optional<String> getCurrentAuditor() {
		return Optional.ofNullable(currentUser.get());
	}

}

NOTE:

Hier wird der in der threadlokalen Variablen festgelegte Benutzername einfach als Ersteller / letzter Aktualisierer der Daten zurückgegeben, jedoch bei der Entwicklung einer tatsächlichen Anwendung, Spring Security usw. Es ist üblich, den von der Authentifizierungsfunktion von verwalteten Login-Benutzernamen zurückzugeben.

Und registrieren Sie es im Anwendungskontext.

@EnableJdbcAuditing
@Import(JdbcConfiguration.class)
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig {
    // ...
    @Bean
    AuditorAware<String> auditorAware() {
        return new MyAuditorAware();
    }
    // ...
}

Wenn Sie die Zeiterfassungsmethode "Wann" gegenüber der Standardimplementierung ändern möchten ("CurrentDateTimeProvider" = "LocalDateTime.now ()"), wenden Sie ein Objekt an, das die Schnittstelle "org.springframework.data.auditing.DateTimeProvider" implementiert. Geben Sie die BeanID des Objekts an, das im Kontext registriert und im Attribut dateTimeProviderRef von @ EnableJdbcAuditing registriert ist.

@EnableJdbcAuditing(dateTimeProviderRef = "dateTimeProvider")
@Import(JdbcConfiguration.class)
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig {
    // ...
    @Bean
    DateTimeProvider dateTimeProvider(ObjectProvider<Clock> clockObjectProvider) {
        return () -> Optional.of(LocalDateTime.now(clockObjectProvider.getIfAvailable(Clock::systemDefaultZone)));
    }
    // ...
}

Hinzufügen von Spalten für die Prüfung

Fügen Sie nach dem Aktivieren der Überwachungsfunktion zunächst eine Überwachungsspalte zur Tabelle hinzu.

CREATE TABLE IF NOT EXISTS todo (
	id IDENTITY
	,title TEXT NOT NULL
	,details TEXT
	,finished BOOLEAN NOT NULL
	,created_at TIMESTAMP
	,created_by VARCHAR(64)
	,last_updated_at TIMESTAMP
	,last_updated_by VARCHAR(64)
);

Verwenden der auf Anmerkungen basierenden Überwachungsfunktion

Ab Spring Data JDBC 1.0.0.RELEASE (Lovelace) wird Annotation-based Auditing Function unterstützt. .RELEASE / reference / html / # auditing.annotations )nur.

Wenn Sie die auf Anmerkungen basierende Überwachungsfunktion verwenden, fügen Sie dem Wert der Überwachungsspalte eine Eigenschaft hinzu und fügen Sie der Zielspalte die folgende Anmerkung hinzu.

~~WARNING:~~

~~ Ab Spring Data JDBC 1.0 M3 (Lovelace) funktioniert es nicht richtig, wenn der Typ der Eigenschaft mit @ Id ein Grundelement ist. (DATAJDBC-216) ~~ -> 2018-05-18 Unterstützt: grinsen:

package com.example.demo.domain;

import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.relational.core.mapping.Column;

import java.time.LocalDateTime;
import java.util.List;

public class Todo {
	@Id
	private int id;
	private String title;
	private String details;
	private boolean finished;

	//Erstellungsdatum und -zeit
	@CreatedDate
	@Column("created_at")
	private LocalDateTime createdAt;

	//Autor
	@CreatedBy
	@Column("created_by")
	private String createdBy;

	//Zuletzt geändert
	@LastModifiedDate
	@Column("last_updated_at")
	private LocalDateTime lastUpdatedAt;

	//Zuletzt aktualisiert
	@LastModifiedBy
	@Column("last_updated_by")
	private String lastUpdatedBy;
	private List<Activity> activities;

	// setters/getters

}

NOTE:

Obwohl nicht mit der Überwachungsfunktion verwandt, ist "@ Column" eine Anmerkung, die nach der Veröffentlichung von Spring Data JDBC 1.0 M3 (Lovelace) (DATAJDBC-106 unterstützt wird. DATA JDBC-106)). ~~ Übrigens, wenn es bestimmte Regeln gibt, wie z. B. die Zuordnung zwischen Schlangenfallspalten und Kamelfall-Eigenschaftsnamen, können Sie NamingStrategy verwenden, um den Unterschied in den Namen zu absorbieren. ~~ (→ DATAJDBC-206 Mit der Unterstützung wird die Zuordnung zwischen dem Namen der Schlangenfallspalte und dem Namen der Kamelfall-Eigenschaft zur Standardoperation. wurde)

Schnittstellenbasierte Überwachungsfunktion

Die schnittstellenbasierte Überwachungsfunktion wird ab Spring Data JDBC 1.0.0.RELEASE (Lovelace) nicht unterstützt. Dies liegt daran, dass Spring Data JDBC die Option "Optional" (DATAJDBC-205) noch nicht unterstützt.

Spring Boot Zusammenarbeit

Vorerst ... Es scheint spring-data-jdbc-boot-Starter im persönlichen Repository des Entwicklers zu geben. Es wird zu diesem Zeitpunkt nirgendwo bereitgestellt, daher müssen Sie es in Ihrem lokalen Repository installieren und verwenden. (Ich habe es diesmal vorerst nicht benutzt) ↓ Spring Boot 2.1 (2.1.0.M4) bietet AutoConfigure und Starter!

NOTE:

Ich schrieb "Try spring-boot-Starter-Daten-jdbc (2.1.0.BUILD-SNAPSHOT)".

Zusammenfassung

Ich denke, es ist eine Bibliothek, die sich noch entwickelt und wächst, deshalb möchte ich die Trends im Auge behalten. Ich halte es für sehr praktisch, wenn Sie benutzerdefinierte Methoden in der Repository-Oberfläche definieren und SQL mit Anmerkungen angeben können (in Bezug auf README scheint es einen Plan zu geben, dies zu unterstützen ⇒ unterstützt !!). Und es wäre großartig, wenn die Zusammenarbeit mit Spring Data REST unterstützt würde. ↓ Endlich wurde 1.0.0 offiziell veröffentlicht! !! Es ist immer noch eine sich entwickelnde Bibliothek, aber ich denke, sie ist im Vergleich zu dem Zeitpunkt, als ich diesen Eintrag schrieb (2018/01/08), sehr gewachsen. [Spring Data REST-Referenz (Spring Data REST 3.1.0.RELEASE)](https://docs.spring.io/spring-data/rest/docs/3.1.0.RELEASE/reference/html/#getting-started In Bezug auf .bootstrap) scheint der offizielle Support noch nicht verfügbar zu sein (ich gebe Ihnen ein Problem ...).

Recommended Posts

Ich habe Spring Data JDBC 1.0.0.BUILD-SNAPSHOT ausprobiert (-> 1.0.0.RELEASE)
[Ich habe es versucht] Spring Tutorial
Ich habe Spring Batch ausprobiert
Ich habe versucht, mit Spring Data JPA zu beginnen
Ich habe Spring State Machine ausprobiert
Ich habe das Spring Boot-Einführungshandbuch [Zugriff auf Daten mit JPA] ausprobiert.
Ich habe versucht, Spring + Mybatis + DbUnit zu verwenden
Ich habe GraphQL mit Spring Boot ausprobiert
Ich habe Flyway mit Spring Boot ausprobiert
Ich habe versucht, mithilfe von JDBC Template mit Spring MVC eine Verbindung zu MySQL herzustellen
Ich habe Lazy Initialization mit Spring Boot 2.2.0 ausprobiert
Ich habe versucht, Tomcat zu setzen
Ich habe youtubeDataApi ausprobiert.
Ich habe versucht, ① umzugestalten
Ich benötige eine Validierung der Federdaten für Pageable ~
Ich habe FizzBuzz ausprobiert.
Ich habe JHipster 5.1 ausprobiert
Ich habe versucht, das Hochladen von Dateien mit Spring MVC zu implementieren
Kompatibilität von Spring JDBC und My Batis mit Spring Data JDBC (vorläufig)
05. Ich habe versucht, die Quelle von Spring Boot zu löschen
Ich habe versucht, die Kapazität von Spring Boot zu reduzieren
Ich habe versucht, Autoware auszuführen
Ich habe versucht, Gson zu benutzen
Ich habe versucht, TestNG zu verwenden
Ich habe versucht, Galasa zu benutzen
Ich habe versucht, node-jt400 (Programme)
Ich habe versucht, node-jt400 (ausführen)
Ich habe versucht, node-jt400 (Transaktionen)
Federdaten Dynamodb-Falle
Versuchen Sie es mit Spring JDBC
[JDBC] Ich habe versucht, von Java aus auf die SQLite3-Datenbank zuzugreifen.
Ich habe versucht, dies und das von Spring @ Transactional zu überprüfen
Ich habe versucht, mit Swagger mit Spring Boot zu beginnen