Ich möchte JavaFX mit DB-Zugriff mit Spring JDBC verwenden. Aber ich bin ganz neu zum Frühling und es scheint, dass ich nicht ganz verstehe es Funktionen, vor allem Transaktionen Kosten ...JavaFX + Spring (JDBC & @ SpringBootApplication & @ Autowired & @Transactional)
Ich habe folgende Abhängigkeiten zu meinem Projekt hinzugefügt:
compile 'org.springframework.boot:spring-boot-starter-jdbc'
runtime 'mysql:mysql-connector-java'
... und Ich möchte den Spring-Transaktions-Mechanismus verwenden, wenn die GUI-Anwendung ihre Operationen auf der DB ausführt. Wie ich es verstehe, folgenden Code sollte:
- initialize und JavaFX-Anwendung starten - erstellen und zeigen GUI Drahtgitter-
- initialisieren Frühling
- konfigurieren und JdbcTemplate Abhängigkeit
- Starttransaktion zur Bearbeitung injizieren und beginnen Transaktion
- Verwenden Sie das jdbcTemplate-Objekt, um 5 Einträge im DB in
for loop
- zu erstellen, simulieren Sie Fehler (durch Werfen
RuntimeException
) - revert Operationen auf DB
- Ausfahrt
Also, zusammenfassend: wenn RuntimeException
in Methode geworfen wird als @Transactional
mit Anmerkungen versehen, die alle Einträge bereits mit dieser Methode erstellt zurückkehren sollte vor der Anwendung beendet wird, nicht wahr?
Alle erstellten Einträge bleiben jedoch dauerhaft in der DB (ich kann sie dort sehen, nachdem die Anwendung beendet wurde). Also zuerst - verstehe ich richtig, wie diese Transaktionen funktionieren sollten? Wenn ja, wie kann ich dann funktionieren, wie ich es erwarte?
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@SpringBootApplication
public class SpringTransactional extends Application {
private Pane viewPane;
private ConfigurableApplicationContext springContext;
/** application.properties:
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=UTC
spring.datasource.username = db_username
spring.datasource.password = username123
*/
@Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) {
launch(args);
}
@Override
public void init() throws Exception {
springContext = SpringApplication.run(SpringTransactional.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
}
@Override
public void stop() throws Exception {
springContext.close();
}
@Override
public void start(Stage primaryStage) {
viewPane = assembleView(primaryStage);
try {
db_transaction_test();
} catch (RuntimeException e) {
e.printStackTrace();
}
Platform.exit();
}
private Pane assembleView(Stage primaryStage) {
VBox rootPane = new VBox();
rootPane.setSpacing(10);
rootPane.setPadding(new Insets(10));
rootPane.setStyle("-fx-base: #84a7ad;");
rootPane.getChildren().add(new Label("GUI goes here."));
primaryStage.setScene(new Scene(rootPane));
primaryStage.setResizable(false);
primaryStage.show();
return rootPane;
}
@Transactional
private void db_transaction_test() {
for (int i = 0; i < 10; i++) {
try {
int entry_name = getEntryId("entry_" + i);
System.out.println("Created entry id=" + entry_name);
} catch (DaoException e) {
e.printStackTrace();
}
if (i == 5) {
throw new RuntimeException("Testing data upload procedure break.");
}
}
}
/** DB creation and schema:
CREATE DATABASE db_name;
CREATE USER db_username;
USE db_name;
GRANT ALL ON db_name.* TO db_username;
SET PASSWORD FOR spz = PASSWORD('username123');
FLUSH PRIVILEGES;
CREATE TABLE Entry (
entry_ID INT NOT NULL AUTO_INCREMENT,
name TEXT NOT NULL,
PRIMARY KEY (entry_ID)
);
*/
private int getEntryId(String entryName) throws DaoException {
List<DbEntry> dbEntries = retrieveEntriesFor(entryName);
if (dbEntries.size() == 1) {
return dbEntries.get(0).getEntry_ID();
} else if (dbEntries.size() == 0) {
String sqlInsert = "INSERT INTO Entry (name) VALUES (?)";
jdbcTemplate.update(sqlInsert, entryName);
dbEntries = retrieveEntriesFor(entryName);
if (dbEntries.size() == 1) {
return dbEntries.get(0).getEntry_ID();
} else {
throw new DaoException("Invalid results amount received after creating new (" + dbEntries.size() + ") when getting entry for name: " + entryName);
}
} else {
throw new DaoException("Invalid results amount received (" + dbEntries.size() + ") when getting entry for name: " + entryName);
}
}
private List<DbEntry> retrieveEntriesFor(String entryName) {
return jdbcTemplate.query("SELECT * FROM Entry WHERE name=?;", (ResultSet result, int rowNum) -> unMarshal(result), entryName);
}
private DbEntry unMarshal(ResultSet result) throws SQLException {
DbEntry dbEntry = new DbEntry();
dbEntry.setEntry_ID(result.getInt("entry_ID"));
dbEntry.setName(result.getString("name"));
return dbEntry;
}
public class DbEntry {
private int entry_ID;
private String name;
int getEntry_ID() { return entry_ID; }
void setEntry_ID(int entry_ID) { this.entry_ID = entry_ID; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
private class DaoException extends Throwable {
DaoException(String err_msg) { super(err_msg); }
}
}
Das typische Setup besteht also darin, dass Sie Transaktionen auf Ihren Business-Tier-Methoden markieren und Ihr DAO in die Business-Schicht einfügen, deren Methoden es dann aufrufen. Aber die tatsächliche Antwort auf Ihre Frage ist dieselbe, egal wie Sie sie strukturieren: '@ Transactional' * funktioniert nur mit Spring-Beans *. In Ihrer Frage haben Sie versucht, es für ein Objekt zu verwenden, das extern für Spring erstellt wurde. –
Und was ist mit: 'springContext.getAutowireCapableBeanFactory(). AutowireBean (this);' in der Frage? Es war in der Lage, '@Autowired private JdbcTemplate jdbcTemplate;' obwohl es "auf einem Objekt, das extern zu Spring erstellt wurde", wie Sie es ... – alwi
Richtig: das im Grunde sagt der Anwendungskontext, alle bekannten Bohnen in Felder mit Anmerkungen zu injizieren '@ Autowire' für das übergebene Objekt (in diesem Fall). Aber es kann dieses Objekt im Speicher tatsächlich nicht durch ein Proxy-Objekt ersetzen. Während also Beans injiziert wird, kann das Verhalten des vorhandenen Objekts nicht geändert werden. Also macht Ihre 'db_transaction_test()' Methode immer noch genau das, wofür Sie sie programmiert haben, nicht mehr und nicht weniger. Es kann die Implementierung einer Methode, die zu einem vorhandenen Objekt gehört, mit dem Transaktionsverhalten nicht magisch verzieren. –