1670930520
In this blog, we will cover accessing data using Micronaut JPA-Hibernate.
It is an open-source JVM-based software framework for building lightweight, modular applications and microservices. Micronaut has the ability to help developers create apps and microservices with small memory footprints and short startup times.
JPA stands for Java Persistence API is a specification that is used to access, manage, and persist data between Java objects and relational databases and is widely considered a standard approach for Object Relational Mapping.
Hibernate is a Java framework used to store Java objects in the relational database system. It is an implementation of JPA and hence, it follows the common standards provided by JPA.
We can use the Micronaut framework to build an application for accessing data from the database with the use of JPA Hibernate. The example below shows the implementation of this:
1.We can directly make a Micronaut application using the below command:
mn create-app example.micronaut.micronaut-jpa-hibernate --build=maven --lang=java
where “example.micronaut.micronaut-jpa-hibernate” is the app name.
2. Now, let’s start the actual coding part by creating one Controller, Repo, RepoImpl, Entity, and the following Test Cases:
Controller class:
Entity Class (also add the following Getters and Setters, Constructors, etc):
Repository:
RepoImpl Class:
Now run the project locally and we can verify the endpoints using postman.
Hence, we have covered the Micronaut Framework using JPA Hibernate with an example.
Original article source at: https://blog.knoldus.com/
1660016280
This is a project to manage my movies. It shows howto use Angular with Spring Boot and Jpa. For development it uses a H2 database. For persistent data it uses Postgresql. The databases are initialized and updated with Liquibase.
Author: Sven Loesekann
Technologies: Angular, Angular-Cli, Ng-Bootstrap, Typescript, Spring Boot, H2, Postgresql, Liquibase, Jpa, Maven, Java
The project serves as example for the integration of Angular, Spring Boot with Jpa and relational databases in clean architecture. The Angular frontend uses the Ng-Bootstrap components. The backend manages/initialzies the H2/Postgresql databases with Liquibase. The data access is done with Jpa and Spring Repositories. The movies are imported with Spring RestTemplates. The architecture is checked with ArchUnit in a test. The security setup is done with Spring Security and Jwt Tokens, that are locked after logout.
In the postgresql.sh file are the commands to pull and run Postgresql in a Docker image locally. To build a Jar with Postgresql setup build it with 'mvnw clean install -Ddocker=true'. In Eclipse the maven profile 'standalone-postgresql' has to be activated and a run/debug configuration with the VM parameter '-Dspring.profiles.active=prod' has to started. The database will be initialized by Liquibase. The Liquibase scripts are setup with preconditions that the tables/sequences/indexes are only created if they do not exist.
In the helm directory is a kubernetes setup to run the moviemanager project with minikube. The Helm chart deployes the postgres database and the moviemanager with the needed parameters to run. It uses the resource limit support of Jdk 16 to limit memory. Kubernetes limits the cpu use and uses the startupprobes and livenessprobes that Spring Actuator provides.
To import movies a key needs to be provided at signin. To get such a key according to this Faq
It is test data provided for the User 'John' and the Password 'Doe'. Then a movie with an actor is available for testing. The login data is also needed for the /h2-console.
The Spring Actuator interface with Prometheus interface can be used as it is described in this article:
Monitoring Spring Boot with Prometheus and Grafana
To test the setup the application has to be started and the Docker Images for Prometheus and Grafana have to be started and configured. The scripts 'runGraphana.sh' and 'runPrometheus.sh' can be used as a starting point.
Postgresql 10.x or newer.
Eclipse IDE for Enterprise Java and Web Developers newest version.
Java 17 or newer
Maven 3.5.2 or newer.
Nodejs 14.15.x or newer
Npm 6.14.x or newer
Angular Cli 14 or newer.
Author: Angular2Guy
Source code: https://github.com/Angular2Guy/MovieManager
License: Apache-2.0 license
#spring #springboot #java #angular #maven #typescript #jpa
1654720440
ロックは、データベース内の同じデータを並行して処理できるようにするメカニズムです。複数のトランザクションが同時に同じデータにアクセスしようとすると、ロックが機能し、これらのトランザクションの1つだけがデータを変更するようになります。JPAは、楽観的モデルと悲観的モデルの2種類のロックメカニズムをサポートしています。
例として航空会社のデータベースを考えてみましょう。このflights
テーブルには、フライトに関する情報とtickets
、予約されたチケットに関する情報が格納されます。各フライトには独自の容量があり、これはflights.capacity
桁。私たちのアプリケーションは、販売されるチケットの数を制御する必要があり、満席のフライトのチケットの購入を許可するべきではありません。これを行うには、チケットを予約するときに、データベースからフライトの容量と販売されたチケットの数を取得する必要があります。フライトに空席がある場合はチケットを販売します。それ以外の場合は、座席をユーザーに通知します。不足しています。各ユーザーリクエストが別々のスレッドで処理されると、データの不整合が発生する可能性があります。フライトに空席が1つあり、2人のユーザーが同時にチケットを予約するとします。この場合、2つのスレッドがデータベースから販売されたチケットの数を同時に読み取り、まだシートが残っていることを確認して、チケットをクライアントに販売します。このような衝突を回避するために、ロックが適用されます。
SpringDataJPAとSpringBootを使用します。エンティティ、リポジトリ、その他のクラスを作成しましょう。
@Entity
@Table(name = "flights")
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
private LocalDateTime departureTime;
private Integer capacity;
@OneToMany(mappedBy = "flight")
private Set<Ticket> tickets;
// ...
// getters and setters
// ...
public void addTicket(Ticket ticket) {
ticket.setFlight(this);
getTickets().add(ticket);
}
}
public interface FlightRepository extends CrudRepository<Flight, Long> { }
@Entity
@Table(name = "tickets")
public class Ticket {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "flight_id")
private Flight flight;
private String firstName;
private String lastName;
// ...
// getters and setters
// ...
}
public interface TicketRepository extends CrudRepository<Ticket, Long> { }
DbService
トランザクションの変更を実行します。
@Service
public class DbService {
private final FlightRepository flightRepository;
private final TicketRepository ticketRepository;
public DbService(FlightRepository flightRepository, TicketRepository ticketRepository) {
this.flightRepository = flightRepository;
this.ticketRepository = ticketRepository;
}
@Transactional
public void changeFlight1() throws Exception {
// the code of the first thread
}
@Transactional
public void changeFlight2() throws Exception {
// the code of the second thread
}
}
アプリケーションクラス:
import org.apache.commons.lang3.function.FailableRunnable;
@SpringBootApplication
public class JpaLockApplication implements CommandLineRunner {
@Resource
private DbService dbService;
public static void main(String[] args) {
SpringApplication.run(JpaLockApplication.class, args);
}
@Override
public void run(String... args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(safeRunnable(dbService::changeFlight1));
executor.execute(safeRunnable(dbService::changeFlight2));
executor.shutdown();
}
private Runnable safeRunnable(FailableRunnable<Exception> runnable) {
return () -> {
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
};
}
}
このデータベースの状態は、以降のアプリケーションの実行ごとに使用されます
flights
テーブル:
id | 番号 | 出発時間 | 容量 |
---|---|---|---|
1 | FLT123 | 2022-04-01 09:00:00 + 03 | 2 |
2 | FLT234 | 2022-04-10 10:30:00 + 03 | 50 |
tickets
テーブル:
id | Flight_id | ファーストネーム | 苗字 |
---|---|---|---|
1 | 1 | ポール | リー |
ロックせずにチケットを同時に購入することをシミュレートするコードを書いてみましょう。
@Service
public class DbService {
// ...
// autowiring
// ...
private void saveNewTicket(String firstName, String lastName, Flight flight) throws Exception {
if (flight.getCapacity() <= flight.getTickets().size()) {
throw new ExceededCapacityException();
}
var ticket = new Ticket();
ticket.setFirstName(firstName);
ticket.setLastName(lastName);
flight.addTicket(ticket);
ticketRepository.save(ticket);
}
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Robert", "Smith", flight);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Kate", "Brown", flight);
Thread.sleep(1_000);
}
}
public class ExceededCapacityException extends Exception { }
呼び出すThread.sleep(1_000);
ことで、両方のスレッドによって開始されたトランザクションが時間的に重複するようになります。データベースでこの例を実行した結果:
id | Flight_id | ファーストネーム | 苗字 |
---|---|---|---|
1 | 1 | ポール | リー |
2 | 1 | ケイト | 茶色 |
3 | 1 | ロバート | スミス |
ご覧のとおり、FLT123便の定員は2名ですが、3枚のチケットが予約されています。
次に、楽観的なブロッキングがどのように機能するかを見てみましょう。より簡単な例から始めましょう-フライト変更の同時容量。楽観的ロックを使用するには、アノテーション付きの永続プロパティを@Version
エンティティクラスに追加する必要があります。このプロパティのタイプint
は、、、、、、、、、またはです。バージョンプロパティは永続性プロバイダーによって管理されます。その値を手動で変更する必要はありません。エンティティが変更されると、バージョン番号が1増加します(または、注釈のあるフィールドがjava.sql.Timestampタイプの場合は、タイムスタンプが更新されます)。また、エンティティを保存するときに元のバージョンがデータベース内のバージョンと一致しない場合、例外がスローされます。IntegershortShortlongLongjava.sql.Timestamp@Version
version
プロパティをFlight
エンティティに追加します
@Entity
@Table(name = "flights")
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
private LocalDateTime departureTime;
private Integer capacity;
@OneToMany(mappedBy = "flight")
private Set<Ticket> tickets;
@Version
private Long version;
// ...
// getters and setters
//
public void addTicket(Ticket ticket) {
ticket.setFlight(this);
getTickets().add(ticket);
}
}
テーブルにversion
列を追加しますflights
id | 名前 | 出発時間 | 容量 | バージョン |
---|---|---|---|---|
1 | FLT123 | 2022-04-01 09:00:00 + 03 | 2 | 0 |
2 | FLT234 | 2022-04-10 10:30:00 + 03 | 50 | 0 |
次に、両方のスレッドの飛行容量を変更します。
@Service
public class DbService {
// ...
// autowiring
// ...
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(10);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(20);
Thread.sleep(1_000);
}
}
アプリケーションを実行すると、例外が発生します
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: update flights set capacity=?, departure_time=?, number=?, version=? where id=? and version=?
したがって、この例では、一方のスレッドが変更を保存し、もう一方のスレッドはデータベースにすでに変更があるため、変更を保存できませんでした。これにより、同じフライトの同時変更が防止されます。例外メッセージでは、句id
でversion
列が使用されていることがわかります。where
属性を使用して@OneToMany
および@ManyToMany
コレクションを変更する場合、バージョン番号は変更されないことに注意してください。mappedBy
元のDbServiceコードを復元して、確認してみましょう。
@Service
public class DbService {
// ...
// autowiring
// ...
private void saveNewTicket(String firstName, String lastName, Flight flight) throws Exception {
if (flight.getCapacity() <= flight.getTickets().size()) {
throw new ExceededCapacityException();
}
var ticket = new Ticket();
ticket.setFirstName(firstName);
ticket.setLastName(lastName);
flight.addTicket(ticket);
ticketRepository.save(ticket);
}
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Robert", "Smith", flight);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Kate", "Brown", flight);
Thread.sleep(1_000);
}
}
アプリケーションは正常に実行され、tickets
表の結果は次のようになります
id | Flight_id | ファーストネーム | 苗字 |
---|---|---|---|
1 | 1 | ポール | リー |
2 | 1 | ロバート | スミス |
3 | 1 | ケイト | 茶色 |
繰り返しになりますが、チケットの数がフライト容量を超えています。
@Lock
JPAを使用すると、値付きのアノテーションを使用してエンティティをロードするときに、バージョン番号を強制的に増やすことができOPTIMISTIC_FORCE_INCREMENT
ます。クラスにfindWithLockingById
メソッドを追加しましょう。FlightRepository
Spring Data JPAでは、find
との間の任意のテキストをBy
メソッド名に追加できます。また、などのキーワードが含まれていない場合Distinct
、テキストは説明的であり、メソッドは通常のように実行されますfind…By…
。
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
Optional<Flight> findWithLockingById(Long id);
}
findWithLockingById
の方法を使用しますDbService
@Service
public class DbService {
// ...
// autowiring
// ...
private void saveNewTicket(String firstName, String lastName, Flight flight) throws Exception {
// ...
}
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findWithLockingById(1L).get();
saveNewTicket("Robert", "Smith", flight);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findWithLockingById(1L).get();
saveNewTicket("Kate", "Brown", flight);
Thread.sleep(1_000);
}
}
アプリケーションが起動すると、2つのスレッドのいずれかが。をスローしObjectOptimisticLockingFailureException
ます。tickets
テーブルの状態は
id | Flight_id | ファーストネーム | 苗字 |
---|---|---|---|
1 | 1 | ポール | リー |
2 | 1 | ロバート | スミス |
今回は1つのチケットのみがデータベースに保存されていることがわかります。
テーブルに新しい列を追加することが不可能であるが、楽観的ロックを使用する必要がある場合は、HibernateアノテーションOptimisticLocking
とを適用できますDynamicUpdate
。OptimisticLockingアノテーションのタイプ値は、次の値を取ることができます。
ALL
-すべてのフィールドに基づいてロックを実行します
DIRTY
-変更されたフィールドフィールドのみに基づいてロックを実行します
VERSION
-専用バージョンの列を使用してロックを実行します
NONE
-ロックを実行しないでください
DIRTY
飛行容量の変更の例では、楽観的ロックタイプを試してみます。
@Entity
@Table(name = "flights")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
private LocalDateTime departureTime;
private Integer capacity;
@OneToMany(mappedBy = "flight")
private Set<Ticket> tickets;
// ...
// getters and setters
// ...
public void addTicket(Ticket ticket) {
ticket.setFlight(this);
getTickets().add(ticket);
}
}
@Service
public class DbService {
// ...
// autowiring
// ...
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(10);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(20);
Thread.sleep(1_000);
}
}
例外がスローされます
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: update flights set capacity=? where id=? and capacity=?
現在id
、cpacity
列はwhere
句で使用されます。ロックタイプをに変更するとALL
、そのような例外がスローされます
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: update flights set capacity=? where id=? and capacity=? and departure_time=? and number=?
これで、すべての列がwhere
句で使用されます。
ペシミスティックロックを使用すると、テーブルの行がデータベースレベルでロックされます。FlightRepository#findWithLockingById
メソッドのブロックタイプを次のように変更しましょうPESSIMISTIC_WRITE
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Flight> findWithLockingById(Long id);
}
予約チケットの例を再実行します。スレッドの1つがスローExceededCapacityException
され、2つのチケットのみがtickets
テーブルに表示されます。
id | Flight_id | ファーストネーム | 苗字 |
---|---|---|---|
1 | 1 | ポール | リー |
2 | 1 | ケイト | 茶色 |
これで、フライトを最初にロードしたスレッドがflights
テーブルの行に排他的にアクセスできるようになったため、2番目のスレッドはロックが解除されるまで作業を一時停止します。最初のスレッドがトランザクションをコミットしてロックを解放した後、2番目のスレッドは行へのモノポールアクセスを取得しますが、最初のスレッドによって行われた変更がデータベースに反映されるため、この時点でフライト容量はすでに使い果たされています。その結果、制御されたExceededCapacityException
例外がスローされます。
JPAには、次の3種類の悲観的ロックがあります。
PESSIMISTIC_READ
-共有ロックを取得します。ロックされたエンティティは、トランザクションがコミットされる前に変更できません。
PESSIMISTIC_WRITE
-排他ロックを取得すると、ロックされたエンティティを変更できます。
PESSIMISTIC_FORCE_INCREMENT
-排他的ロックを取得してバージョン列を更新すると、ロックされたエンティティを変更できます
多くのスレッドがデータベース内の同じ行をロックしている場合、ロックを取得するのに長い時間がかかる場合があります。ロックを受信するためのタイムアウトを設定できます。
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="10000")})
Optional<Flight> findWithLockingById(Long id);
}
タイムアウトが経過すると、CannotAcquireLockException
がスローされます。すべての永続性プロバイダーがヒントをサポートしているわけではないことに注意することが重要ですjavax.persistence.lock.timeout
。たとえば、Oracleの永続性プロバイダーはこのヒントをサポートしていますが、PostgreSQL、MS SQL Server、MySQL、およびH2はサポートしていません。
ここで、デッドロック状態を検討します。
@Service
public class DbService {
// ...
// autowiring
// ...
private void fetchAndChangeFlight(long flightId) throws Exception {
var flight = flightRepository.findWithLockingById(flightId).get();
flight.setCapacity(flight.getCapacity() + 1);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight1() throws Exception {
fetchAndChangeFlight(1L);
fetchAndChangeFlight(2L);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
fetchAndChangeFlight(2L);
fetchAndChangeFlight(1L);
Thread.sleep(1_000);
}
}
スレッドの1つから次のスタックトレースを取得します
org.springframework.dao.CannotAcquireLockException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not extract ResultSet
...
Caused by: org.postgresql.util.PSQLException: ERROR: deadlock detected
...
データベースは、このコードがデッドロックにつながることを検出しました。ただし、データベースがこれを実行できず、タイムアウトが終了するまでスレッドが実行を一時停止する場合があります。
楽観的および悲観的ロックは、2つの異なるアプローチです。オプティミスティックロックは、スローされた例外を簡単に処理して、ユーザーに通知するか、再試行できる状況に適しています。同時に、データベースレベルの行はブロックされないため、アプリケーションの動作が遅くなることはありません。ブロックを取得できた場合、ペシミスティックロックは、データベースへのクエリの実行を大幅に保証します。ただし、ペシミスティックロックを使用する場合は、デッドロックが発生する可能性があるため、コードを慎重に記述して確認する必要があります。デッドロックは、検出と修正が困難なフローティングエラーになる可能性があります。
このストーリーは、もともとhttps://hackernoon.com/optimistic-and-pessimistic-locking-in-jpaで公開されました
#optimistic #pessimistic #jpa
1654718400
El bloqueo es un mecanismo que permite trabajar en paralelo con los mismos datos en la base de datos. Cuando más de una transacción intenta acceder a los mismos datos al mismo tiempo, entran en juego los bloqueos, lo que garantiza que solo una de estas transacciones cambiará los datos. JPA admite dos tipos de mecanismos de bloqueo: modelo optimista y modelo pesimista.
Consideremos la base de datos de la aerolínea como ejemplo. La flights
tabla almacena información sobre vuelos y tickets
almacena información sobre boletos reservados. Cada vuelo tiene su propia capacidad, que se almacena en elflights.capacity
columna. Nuestra aplicación debe controlar la cantidad de boletos vendidos y no debe permitir comprar un boleto para un vuelo completo. Para ello, al momento de reservar un boleto, necesitamos obtener de la base de datos la capacidad del vuelo y el número de boletos vendidos, y si hay asientos vacíos en el vuelo, vender el boleto, de lo contrario, informar al usuario que los asientos se han agotado Si cada solicitud de usuario se procesa en un subproceso independiente, es posible que se produzcan incoherencias en los datos. Suponga que hay un asiento vacío en el vuelo y dos usuarios reservan boletos al mismo tiempo. En este caso, dos subprocesos leen simultáneamente el número de boletos vendidos de la base de datos, verifican que aún queda un asiento y venden el boleto al cliente. Para evitar tales colisiones, se aplican bloqueos.
Usaremos Spring Data JPA y Spring Boot. Vamos a crear entidades, repositorios y otras clases:
@Entity
@Table(name = "flights")
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
private LocalDateTime departureTime;
private Integer capacity;
@OneToMany(mappedBy = "flight")
private Set<Ticket> tickets;
// ...
// getters and setters
// ...
public void addTicket(Ticket ticket) {
ticket.setFlight(this);
getTickets().add(ticket);
}
}
public interface FlightRepository extends CrudRepository<Flight, Long> { }
@Entity
@Table(name = "tickets")
public class Ticket {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "flight_id")
private Flight flight;
private String firstName;
private String lastName;
// ...
// getters and setters
// ...
}
public interface TicketRepository extends CrudRepository<Ticket, Long> { }
DbService
realiza cambios transaccionales:
@Service
public class DbService {
private final FlightRepository flightRepository;
private final TicketRepository ticketRepository;
public DbService(FlightRepository flightRepository, TicketRepository ticketRepository) {
this.flightRepository = flightRepository;
this.ticketRepository = ticketRepository;
}
@Transactional
public void changeFlight1() throws Exception {
// the code of the first thread
}
@Transactional
public void changeFlight2() throws Exception {
// the code of the second thread
}
}
Una clase de aplicación:
import org.apache.commons.lang3.function.FailableRunnable;
@SpringBootApplication
public class JpaLockApplication implements CommandLineRunner {
@Resource
private DbService dbService;
public static void main(String[] args) {
SpringApplication.run(JpaLockApplication.class, args);
}
@Override
public void run(String... args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(safeRunnable(dbService::changeFlight1));
executor.execute(safeRunnable(dbService::changeFlight2));
executor.shutdown();
}
private Runnable safeRunnable(FailableRunnable<Exception> runnable) {
return () -> {
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
};
}
}
Usaremos este estado de la base de datos en cada ejecución siguiente de la aplicación.
flights
mesa:
identificación | número | hora de salida | capacidad |
---|---|---|---|
1 | FLT123 | 2022-04-01 09:00:00+03 | 2 |
2 | FLT234 | 2022-04-10 10:30:00+03 | 50 |
tickets
mesa:
identificación | id_vuelo | primer nombre | apellido |
---|---|---|---|
1 | 1 | Pablo | Sotavento |
Escribamos un código que simule la compra simultánea de entradas sin bloqueo.
@Service
public class DbService {
// ...
// autowiring
// ...
private void saveNewTicket(String firstName, String lastName, Flight flight) throws Exception {
if (flight.getCapacity() <= flight.getTickets().size()) {
throw new ExceededCapacityException();
}
var ticket = new Ticket();
ticket.setFirstName(firstName);
ticket.setLastName(lastName);
flight.addTicket(ticket);
ticketRepository.save(ticket);
}
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Robert", "Smith", flight);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Kate", "Brown", flight);
Thread.sleep(1_000);
}
}
public class ExceededCapacityException extends Exception { }
La llamada Thread.sleep(1_000);
se asegura de que las transacciones iniciadas por ambos subprocesos se superpongan en el tiempo. El resultado de ejecutar este ejemplo en la base de datos:
identificación | id_vuelo | primer nombre | apellido |
---|---|---|---|
1 | 1 | Pablo | Sotavento |
2 | 1 | Kate | Marrón |
3 | 1 | Roberto | Herrero |
Como puede ver, se reservaron tres boletos, aunque la capacidad del vuelo FLT123 es de dos pasajeros.
Ahora, mira cómo funciona el bloqueo optimista. Comencemos con un ejemplo más sencillo: una capacidad simultánea del cambio de vuelo. Para usar el bloqueo optimista, @Version
se debe agregar una propiedad persistente con una anotación a la clase de entidad. Esta propiedad puede ser de tipo int
, Integer
, short
, Short
, long
, Long
o java.sql.Timestamp
. La propiedad de versión es administrada por el proveedor de persistencia, no necesita cambiar su valor manualmente. Si se cambia la entidad, el número de versión aumenta en 1 (o se actualiza la marca de tiempo si el campo con la @Version
anotación es del tipo java.sql.Timestamp). Y si la versión original no coincide con la versión en la base de datos al guardar la entidad, se lanza una excepción.
Agregar la version
propiedad a la Flight
entidad
@Entity
@Table(name = "flights")
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
private LocalDateTime departureTime;
private Integer capacity;
@OneToMany(mappedBy = "flight")
private Set<Ticket> tickets;
@Version
private Long version;
// ...
// getters and setters
//
public void addTicket(Ticket ticket) {
ticket.setFlight(this);
getTickets().add(ticket);
}
}
Agregar la version
columna a la flights
tabla
identificación | nombre | hora de salida | capacidad | versión |
---|---|---|---|---|
1 | FLT123 | 2022-04-01 09:00:00+03 | 2 | 0 |
2 | FLT234 | 2022-04-10 10:30:00+03 | 50 | 0 |
Ahora cambiamos la capacidad de vuelo en ambos hilos:
@Service
public class DbService {
// ...
// autowiring
// ...
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(10);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(20);
Thread.sleep(1_000);
}
}
Ahora al ejecutar nuestra aplicación obtendremos una excepción
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: update flights set capacity=?, departure_time=?, number=?, version=? where id=? and version=?
Por lo tanto, en nuestro ejemplo, un subproceso guardó los cambios y el otro subproceso no pudo guardar los cambios porque ya hay cambios en la base de datos. Gracias a esto se evitan cambios simultáneos de un mismo vuelo. En el mensaje de excepción, vemos que las columnas id
y version
se usan en la where
cláusula.
Tenga en cuenta que el número de versión no cambia al cambiar las colecciones @OneToMany
y @ManyToMany
con el mappedBy
atributo. Restauremos el código original de DbService y compruébelo:
@Service
public class DbService {
// ...
// autowiring
// ...
private void saveNewTicket(String firstName, String lastName, Flight flight) throws Exception {
if (flight.getCapacity() <= flight.getTickets().size()) {
throw new ExceededCapacityException();
}
var ticket = new Ticket();
ticket.setFirstName(firstName);
ticket.setLastName(lastName);
flight.addTicket(ticket);
ticketRepository.save(ticket);
}
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Robert", "Smith", flight);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
saveNewTicket("Kate", "Brown", flight);
Thread.sleep(1_000);
}
}
La aplicación se ejecutará con éxito y el resultado en la tickets
tabla será el siguiente
identificación | id_vuelo | primer nombre | apellido |
---|---|---|---|
1 | 1 | Pablo | Sotavento |
2 | 1 | Roberto | Herrero |
3 | 1 | Kate | Marrón |
De nuevo, el número de billetes supera la capacidad de vuelo.
JPA permite aumentar por la fuerza el número de versión al cargar una entidad utilizando la @Lock
anotación con el OPTIMISTIC_FORCE_INCREMENT
valor. Agreguemos el findWithLockingById
método a la FlightRepository
clase. En Spring Data JPA, cualquier texto entre find
y By
se puede agregar al nombre del método, y si no contiene palabras clave como Distinct
, el texto es descriptivo y el método se ejecuta como regular find…By…
:
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
Optional<Flight> findWithLockingById(Long id);
}
Usa el findWithLockingById
método enDbService
@Service
public class DbService {
// ...
// autowiring
// ...
private void saveNewTicket(String firstName, String lastName, Flight flight) throws Exception {
// ...
}
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findWithLockingById(1L).get();
saveNewTicket("Robert", "Smith", flight);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findWithLockingById(1L).get();
saveNewTicket("Kate", "Brown", flight);
Thread.sleep(1_000);
}
}
Cuando se inicia la aplicación, uno de los dos hilos lanza ObjectOptimisticLockingFailureException
. El estado de la tickets
mesa es
identificación | id_vuelo | primer nombre | apellido |
---|---|---|---|
1 | 1 | Pablo | Sotavento |
2 | 1 | Roberto | Herrero |
Vemos que esta vez solo se ha guardado un Ticket en la base de datos.
Si es imposible agregar una nueva columna a la tabla, pero es necesario usar el bloqueo optimista, puede aplicar anotaciones de Hibernate OptimisticLocking
y DynamicUpdate
. El valor de tipo en la anotación OptimisticLocking puede tomar los siguientes valores:
ALL
- realizar el bloqueo basado en todos los campos
DIRTY
- realizar el bloqueo basado solo en campos de campos modificados
VERSION
- realizar el bloqueo usando una columna de versión dedicada
NONE
- no realizar el bloqueo
Probaremos el DIRTY
tipo de bloqueo optimista en el ejemplo de cambio de capacidad de vuelo.
@Entity
@Table(name = "flights")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
private LocalDateTime departureTime;
private Integer capacity;
@OneToMany(mappedBy = "flight")
private Set<Ticket> tickets;
// ...
// getters and setters
// ...
public void addTicket(Ticket ticket) {
ticket.setFlight(this);
getTickets().add(ticket);
}
}
@Service
public class DbService {
// ...
// autowiring
// ...
@Transactional
public void changeFlight1() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(10);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
var flight = flightRepository.findById(1L).get();
flight.setCapacity(20);
Thread.sleep(1_000);
}
}
Se lanzará una excepción.
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: update flights set capacity=? where id=? and capacity=?
Ahora las columnas id
y cpacity
se utilizan en la where
cláusula. Si cambia el tipo de bloqueo a ALL
, se lanzará una excepción de este tipo.
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: update flights set capacity=? where id=? and capacity=? and departure_time=? and number=?
Ahora todas las columnas se usan en la where
cláusula.
Con el bloqueo pesimista, las filas de la tabla se bloquean en el nivel de la base de datos. Cambiemos el tipo de bloqueo del FlightRepository#findWithLockingById
método aPESSIMISTIC_WRITE
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Flight> findWithLockingById(Long id);
}
y vuelva a ejecutar el ejemplo de reserva de entradas. Uno de los hilos tirará ExceededCapacityException
y sólo quedarán dos billetes en la tickets
mesa.
identificación | id_vuelo | primer nombre | apellido |
---|---|---|---|
1 | 1 | Pablo | Sotavento |
2 | 1 | Kate | Marrón |
Ahora, el subproceso que primero cargó el vuelo tiene acceso exclusivo a la fila de la flights
tabla, por lo que el segundo subproceso suspende su trabajo hasta que se libera el bloqueo. Después de que el primer subproceso confirme la transacción y libere el bloqueo, el segundo subproceso obtendrá acceso monopolar a la fila, pero en este punto, la capacidad de vuelo ya estará agotada, porque los cambios realizados por el primer subproceso entrarán en la base de datos. Como resultado, ExceededCapacityException
se lanzará la excepción controlada.
Hay tres tipos de bloqueo pesimista en JPA:
PESSIMISTIC_READ
- adquiere un bloqueo compartido, y la entidad bloqueada no se puede cambiar antes de la confirmación de una transacción.
PESSIMISTIC_WRITE
- adquirir un bloqueo exclusivo, y la entidad bloqueada se puede cambiar.
PESSIMISTIC_FORCE_INCREMENT
- adquirir un bloqueo exclusivo y actualizar la columna de versión, la entidad bloqueada se puede cambiar
Si muchos subprocesos bloquean la misma fila en la base de datos, puede llevar mucho tiempo obtener el bloqueo. Puede establecer un tiempo de espera para recibir un bloqueo:
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="10000")})
Optional<Flight> findWithLockingById(Long id);
}
Si expira el tiempo de espera, CannotAcquireLockException
se lanzará. Es importante tener en cuenta que no todos los proveedores de persistencia admiten la sugerencia javax.persistence.lock.timeout
. Por ejemplo, el proveedor de persistencia de Oracle admite esta sugerencia, mientras que no lo hace para PostgreSQL, MS SQL Server, MySQL y H2.
Ahora consideramos una situación de punto muerto.
@Service
public class DbService {
// ...
// autowiring
// ...
private void fetchAndChangeFlight(long flightId) throws Exception {
var flight = flightRepository.findWithLockingById(flightId).get();
flight.setCapacity(flight.getCapacity() + 1);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight1() throws Exception {
fetchAndChangeFlight(1L);
fetchAndChangeFlight(2L);
Thread.sleep(1_000);
}
@Transactional
public void changeFlight2() throws Exception {
fetchAndChangeFlight(2L);
fetchAndChangeFlight(1L);
Thread.sleep(1_000);
}
}
Obtendremos el siguiente seguimiento de pila de uno de los subprocesos
org.springframework.dao.CannotAcquireLockException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not extract ResultSet
...
Caused by: org.postgresql.util.PSQLException: ERROR: deadlock detected
...
La base de datos detectó que este código conduce a un interbloqueo. Sin embargo, puede haber situaciones en las que la base de datos no pueda hacer esto y los subprocesos suspendan su ejecución hasta que finalice el tiempo de espera.
El bloqueo optimista y pesimista son dos enfoques diferentes. Los bloqueos optimistas son adecuados para situaciones en las que se puede manejar fácilmente una excepción que se ha producido y notificar al usuario o volver a intentarlo. Al mismo tiempo, las filas a nivel de base de datos no se bloquean, lo que no ralentiza el funcionamiento de la aplicación. Si fuera posible obtener un bloque, los bloqueos pesimistas dan grandes garantías para la ejecución de consultas a la base de datos. Sin embargo, al usar el bloqueo pesimista, debe escribir y verificar cuidadosamente el código porque existe la posibilidad de interbloqueos, que pueden convertirse en errores flotantes que son difíciles de encontrar y corregir.
Esta historia se publicó originalmente en https://hackernoon.com/optimistic-and-pessimistic-locking-in-jpa
1650854820
In this video, I'd like to share with you how to write code that connects to H2 database in a Spring Boot application, with Spring JDBC (JdbcTemplate) and Spring Data JPA (with Hibernate).
You will also learn how to connect to H2 database in 3 different mode: in-memory, embedded and client/server.
Software programs: Java Development Kit (JDK); Spring Tool Suite IDE (STS)
1650423420
In this lesson we will have a detailed walkthrough on the Hibernate Automatic dirty checking and the Persistence context. One of the frequently asked question in Hibernate framework Interview is the Update Vs Merge method and the way things work. We will cover this in a much detailed manner along with the Log4j integration with the Hibernate.
Timestamp :
Introduction - 00:00:00
Log4J integration with Hibernate - 00:03:18
Persistence Context & Hibernate Automatic Dirty Checking - 00:11:08
Hibernate Internals ( Snapshots ) - 00:20:42
Update Vs Merge method in JPA / Hibernate - 00:46:58
NonUniqueObjectException and the solution - 01:02:18
Prepare the above if you looking for hibernate interview questions for experienced category as well.
1650248220
In this Spring Data JPA video, I'd love to share with you some code examples about native query. In details, you will learn:
- What is native query?
- When using native query?
- How to code native queries with Spring Data JPA: Select native query; Update native query; and how to use pagination in native queries
I hope will find this video helpful. You can also find code examples in the written article at https://www.codejava.net/frameworks/spring-boot/spring-data-jpa-native-query-examples
1650163920
So, We are all set to program our first java hibernate CRUD application , Step by Step. We will explore different api methods present inside Hibernate / JPA api methods to fetch/load, Read, Delete and update data inside a java app.
After the first 50 mins of this lesson , We will try to think about a problem by doing some debugging. The problem will leads us to create a Singleton session factory by following the GOF singleton design pattern. So Sit back, Relax , a lot to do.. Here goes the time stamp.
introduction : 00:00:00
Recap : Hibernate Concepts explained: 00:02:32
Difference Between configure() vs Configure(..) : 00:07:18
Object mapping (ORM) in action : 00:13:15
Need of @Table: 00:15:19
Hibernate Create using save() :
Hibernate Read Operation : 00:21:06
Hibernate Read using get() : 00:24:29
Hibernate Read using load() : 00:27:07
Hibernate Update using update() : 00:32:42
Hibernate Delete using delete() : 00:45:10
Code Improvement - The Problem : 00:54:37
Design a singleton SessionFactory : 01:07:08
Testing code with debugging : 01:12:15
Fun fact : How the update is working here ? : 01:19:32
Handle Exception in Hibernate Framework Java : 01:23:00
1650161040
Hi fellow programmers, in this video, I'd love to help you understand the semantics of Persist and Merge operations of an entity manager - as defined by Java Persistence API (JPA).
In details, you'll understand:
- Understand the semantics of perist and merge operations
- Code examples with Spring Boot & Spring Data JPA-
- Differences between persist & merge
- When to use persist & merge in your application
Hope you'll find this video helpful as a source to have better understand about using EntityManager in Spring Data JPA.
You can also see code examples in the written article at https://www.codejava.net/frameworks/spring-boot/jpa-entitymanager-persist-vs-merge
1650077460
This Lesson will get you started with the Java Hibernate - The ORM Framework. The goal of this lesson is to build your foundation and help you to write your first hibernate application from scratch. By the end of this lesson you will learn to write a hibernate config file by writing it from absolute scratch and, you will be having a clear cut understanding on different components on hibernate like Entity, Mappings , Configuration , Session, SessionFactory, Transaction and etc.
In this lesson we will code, we will get dirty with different hibernate exceptions and, we will also learn to fix those exceptions. This will help us to solve more real time problem and help us building a solid foundation on hibernate ORM.
Introduction : 00:00:00
What is ORM? : 00:00:49
What is JPA ? : 00:06:24
Hibernate Vs JPA : 00:14:44
What is Hibernate in java ? 00:19:29
Why Hibernate is popular ? 00:21:33
Creating a DB table : 00:22:26
Create a Maven project : 00:24:20
Tutorial goal : Save an java object state to mysql : 00:25:28
Adding Hibernate Dependency in maven: 00:27:55
Hibernate Configuration Object : 00:32;35
Creating a hibernate config file: 00:36:23
Hibernate Session Factory Object: 00:40:46
Hibernate config Properties from Environment & Available settings: 00:44:38
Adding MySQL dependancy: 00:50:53
Resolving Hibernate Mapping Exception : Unknown Entity : 00:57:33
@Entity Introduction : 00:58:32
Resolving Hibernate Annotation Exception: No Identifier specified: 01:02:22
Introducing @Id: 01:03:00
Hibernate Query Generation (show_sql): 01:05:04
SQLException : No database selected 01:07:45
SQLSyntaxErrorException: Unknown Column in the field list
Introducing @Column - Hibernate mapping db columns with properties: 01:11:26
Why Session Factory is heavyweight object ? : 01:16:25
Session Vs SessionFactory : 01:26:07
Doubt Discussion : 01:34:32
Hibernate Default connection pool : 01:35:14
How to install hibernate helper plugin : 01:44:50
Just like Spring, Hibernate is also a vast framework which needs dedication and practice to learn and master. Try to create a fresh java maven project and try to practice this hibernate lesson side by side as well.
As it’s a hibernate step by step tutorial for beginners, I will be looking forward to installing the hibernate plugin and giving a demo during the end of this video. Make sure to follow the appendix if you don’t have the hibernate plugin installed.
1650042120
In this video, I'd love to share with you how to use EntityManager for performing CRUD operations on a MySQL database in a Spring Boot application with Spring Data JPA.
In details, you will learn:
- What is EntityManager?
- When & Where using EntityManager in Spring Data JPA
- Code a Spring Boot project that demonstrates the Create, Update, Retrieve & Delete (CRUD) operations using EntityManager
Technologies: Spring Boot, Spring Data JPA, Hibernate, MySQL JDBC driver
Software programs: Java Development Kit, Spring Tool Suite IDE, MySQL database server, MySQL Workbench.
To copy code examples in this video, read the written article at: https://www.codejava.net/frameworks/spring-boot/spring-data-jpa-entitymanager-examples
1648438023
This Spring Boot Master Class covers a wide range of topics to Master Spring and Spring Boot Framework and accelerate your growth in your career.
We will understand all below topics in detail in this Spring Boot complete Course [Master Class/Boot Camp]
📚Course Content📚
⌚ (00:00) Intro
⌚ (07:32) Why Frameworks?
⌚ (08:47) What is Spring Framework?
⌚ (53:12) What is AOP?
⌚ (56:42) Understanding AOP with Demo Example
⌚ (01:03:41) Logging Aspect example
⌚ (01:10:57) Authentication Aspect example
⌚ (01:22:40) Spring Boot
⌚ (04:33:30) Spring Data JPA
⌚ (06:53:30) Spring Security
🗒 Spring Boot Github: https://github.com/shabbirdwd53/Springboot-Tutorial
🗒 Spring Data JPA Github: https://github.com/shabbirdwd53/Spring-Data-JPA-Tutorial
🗒 Spring Security Code: https://github.com/shabbirdwd53/spring-security-tutorial
JPA Query Documentation: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
IntelliJ Idea Community Edition: https://www.jetbrains.com/idea/download/#section=windows
#springboot #springframework #springsecurity #springdata #jpa #programming #developer #java
1641474122
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Implementing a data access layer of an application has been cumbersome for quite a while. Too much boilerplate code has to be written to execute simple queries as well as perform pagination, and auditing. Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.
This project is governed by the Spring Code of Conduct. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
Here is a quick teaser of an application using Spring Data Repositories in Java:
public interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findByLastname(String lastname);
List<Person> findByFirstnameLike(String firstname);
}
@Service
public class MyService {
private final PersonRepository repository;
public MyService(PersonRepository repository) {
this.repository = repository;
}
public void doWork() {
repository.deleteAll();
Person person = new Person();
person.setFirstname("Oliver");
person.setLastname("Gierke");
repository.save(person);
List<Person> lastNameResults = repository.findByLastname("Gierke");
List<Person> firstNameResults = repository.findByFirstnameLike("Oli*");
}
}
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class AppConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.H2);
jpaVendorAdapter.setGenerateDdl(true);
return jpaVendorAdapter;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lemfb = new LocalContainerEntityManagerFactoryBean();
lemfb.setDataSource(dataSource());
lemfb.setJpaVendorAdapter(jpaVendorAdapter());
lemfb.setPackagesToScan("com.acme");
return lemfb;
}
}
Add the Maven dependency:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${version}.RELEASE</version>
</dependency>
If you’d rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare the appropriate dependency version.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${version}.BUILD-SNAPSHOT</version>
</dependency>
<repository>
<id>spring-libs-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
Having trouble with Spring Data? We’d love to help!
spring-data-jpa
. You can also chat with the community on Gitter.Spring Data uses GitHub as issue tracking system to record bugs and feature requests. If you want to raise an issue, please follow the recommendations below:
You don’t need to build from source to use Spring Data (binaries in repo.spring.io), but if you want to try out the latest and greatest, Spring Data can be easily built with the maven wrapper. You also need JDK 1.8.
$ ./mvnw clean install
If you want to build with the regular mvn
command, you will need Maven v3.5.0 or above.
Also see CONTRIBUTING.adoc if you wish to submit pull requests, and in particular please sign the Contributor’s Agreement before your first non-trivial change.
Building the documentation builds also the project without running tests.
$ ./mvnw clean install -Pdistribute
The generated documentation is available from target/site/reference/html/index.html
.
The spring.io site contains several guides that show how to use Spring Data step-by-step:
Accessing Data with JPA: Learn how to work with JPA data persistence using Spring Data JPA.
Accessing JPA Data with REST is a guide to creating a REST web service exposing data stored with JPA through repositories.
Spring Data Examples contains example projects that explain specific features in more detail.
Download Details:
Author: spring-projects
Source Code: https://github.com/spring-projects/spring-data-jpa
License: Apache-2.0 License
1641013080
In this video you will learn how to solve LazyInitializationException error when working with JPA and Hibernate in Spring Boot and Spring Data applications.
1639989360
When it comes to testing, one has to declare test data. It can be items in the, records in the database, or messages in the distributed queue. The behavior cannot be tested without data. So, today we’re discussing the patterns of creating test rows in the database with Spring Boot + Spring Data + JPA usage.