Première partie Deuxième partie
Application Java
Organisation à 3 niveaux
Revenons à l'application Java. La version de la partie précédente a été créée dans le style HelloWorld pour contrôler l'exactitude des actions initiales. Nous implémentons une architecture à trois niveaux (trois couches), souvent appelée dans la littérature anglophone 3tier/3layer . Sa brève essence est la suivante :- Toutes les entités sont conçues comme des modèles. Ce sont des objets qui contiennent :
- Un ensemble d'attributs (champs privés de la classe).
- Constructeur(s).
- Setters et getters pour la définition/lecture des attributs.
- Il est important qu’ils ne contiennent aucun autre code que celui ci-dessus. De tels objets sont souvent appelés POJO (Plain Old Java Object).
- Toute la logique permettant de travailler avec des modèles est implémentée par la couche Service. Il génère des règles métier pour les modèles. Par exemple, traiter les requêtes d'une application Java. Les arguments de requête et les résultats renvoyés incluent souvent des modèles (ou des collections de ceux-ci).
- La couche Repository est un « intermédiaire » entre le SGBD et le Service, travaillant directement avec la base de données et responsable de l'interaction avec elle.
Modèle
Toutes nos entités (actions, traders, taux et actions des traders) et leurs équivalents de table ont une caractéristique commune : une clé primaire artificielle. Alors créons une classe de baseBaseModel
. Tous les modèles en hériteront.
package sql.demo.model;
import java.util.Objects;
// Базовый класс модели, имеющий ключ id
public class BaseModel {
protected long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public BaseModel() {}
public BaseModel(long id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BaseModel baseModel = (BaseModel) o;
return id == baseModel.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Vous trouverez ci-dessous un exemple de modèle de stock. Vous pouvez voir le reste des listes de modèles en suivant le lien vers le référentiel github à la fin de l'article.
package sql.demo.model;
import java.math.BigDecimal;
// Модель акции
public class Share extends BaseModel{
// поля SQL таблицы и соответствующие им поля модели
// типы данных SQL
private String name; // Наименование
private BigDecimal startPrice; // Начальная цена
private int changeProbability; // Вероятность смены курса (в процентах)
private int delta; // Максимальное колебание (в процентах)стоимости акции в результате торгов
public Share() {
}
public Share(long id, String name, BigDecimal startPrice, int changeProbability, int delta) {
super(id);
this.name = name;
this.startPrice = startPrice;
this.changeProbability = changeProbability;
this.delta = delta;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
... оставшиеся сеттеры/геттеры
}
JDBC
Dans la première partie, nous avons appris comment établir une connexion à la base de données et la fermer. Passons maintenant à autre chose. Les étapes de travail avec JDBC sont présentées dans le schéma suivant :Class.forName()
charge la classe et l'enregistre auprès de DriverManager ;DriverManager.getConnection()
renverraConnection
– une connexion à la base de données spécifiée dans l’argument de la méthode et utilisant le pilote JDBC correspondant (qui a été chargé à l’aide deClass.forName()
).createStatement()
nous renverraStatement
un objet sur la base duquel nous pourrons former des requêtes vers la base de données. Il y a aussi:PreparedStatement
faciliter la création de requêtes paramétrées et batch.
CallableStatement
pour appeler les propres fonctions et procédures SQL du SGBD (on les appelle stockées).- Avoir « en main » vous
statement
permettraexecute()
d'envoyer une requête sous la forme d'une commande du langage de requête SQL directement à l'exécution du SGBD et de renvoyer une réponse sous la forme deResultSet
. Pour plus de commodité, il y a :executeQuery()
– pour lire les données du SGBD.
executeUpdate()
– de modifier les données dans le SGBD. - La réponse du serveur elle-même
ResultSet
peut être traitée sous la forme en itérant surfirst()
,last()
,next()
etc. Nous pouvons obtenir des champs de résultats individuels via des getters :getInteger()
,getString()
...
ResultSet
, Statement
et Connection
d'économiser des ressources. N'oubliez pas que lorsque vous fermez un objet situé plus haut dans la séquence du diagramme, vous fermerez en cascade tous les objets générés au cours du processus de travail avec lui. Ainsi, la fermeture d'une connexion entraînera la fermeture de l'ensemble de celle-ci Statement
et de tout ce ResultSet
qui a été reçu avec leur aide.
Implémentation du référentiel
Après la partie théorique JDBC, passons à la mise en œuvre du référentiel. Nous l'implémentons architecturalement comme suit :- Nous déplacerons les parties les plus générales du travail avec un SGBD vers un ancêtre commun -
BaseTable
; - Les opérations logiques que nous effectuerons seront déclarées dans l'interface
TableOperation
;
BaseTable
et implémentera l'interface TableOperation
. Nous devons donc écrire l'implémentation des méthodes déclarées dans l'interface TableOperation
. Dans ce cas, nous pouvons utiliser les méthodes de la classe parent BaseTable
. Pour le moment, l'interface déclare les méthodes de création de tables :
package sql.demo.repository;
import java.sql.SQLException;
// Операции с tableми
public interface TableOperations {
void createTable() throws SQLException; // создание таблицы
void createForeignKeys() throws SQLException; // создание связей между tableми
void createExtraConstraints() throws SQLException; // создание дополнительных правил для значений полей таблиц
}
Au fur et à mesure que vous étudiez le matériel, la liste des déclarations de méthode s'allongera ( read()
, update()
….). Nous implémenterons les nouvelles fonctionnalités en deux étapes :
- Ajoutons une autre possibilité de travailler avec un tableau sous la forme d'une nouvelle méthode d'interface.
- Ensuite, dans les classes implémentant l'interface, nous décrirons l'implémentation logicielle dans les nouvelles méthodes générées par l'interface.
Share
(stocks). La logique principale réside dans les commandes permettant de créer des tables, de spécifier les types de données SQL pour les champs et d'ajouter des restrictions :
package sql.demo.repository;
import java.sql.SQLException;
public class Shares extends BaseTable implements TableOperations{
public Shares() throws SQLException {
super("shares");
}
@Override
public void createTable() throws SQLException {
super.executeSqlStatement("CREATE TABLE shares(" +
"id BIGINT AUTO_INCREMENT PRIMARY KEY," +
"name VARCHAR(255) NOT NULL," +
"startPrice DECIMAL(15,2) NOT NULL DEFAULT 10," +
"changeProbability INTEGER NOT NULL DEFAULT 25,"+
"delta INTEGER NOT NULL DEFAULT 15)", "Создана table " + tableName);
}
@Override
public void createForeignKeys() throws SQLException {
}
@Override
public void createExtraConstraints() throws SQLException {
super.executeSqlStatement(
" ALTER TABLE shares ADD CONSTRAINT check_shares_delta CHECK(delta <= 100 and delta > 0)",
"Cоздано ограничение для shares, поле delta = [1,100]");
super.executeSqlStatement(
" ALTER TABLE shares ADD CONSTRAINT check_shares_changeProbability " +
"CHECK(changeProbability <= 100 and changeProbability > 0)",
"Cоздано ограничение для shares, поле changeProbability = 1..100");
}
}
Les listes d'autres référentiels et de la classe parent sont disponibles via le lien vers le référentiel github à la fin de l'article. Bien sûr, vous pouvez concevoir un programme différent ou refactoriser le programme plus en profondeur : déplacer les parties communes dans une classe parent, mettre en évidence les méthodes communes, etc. Mais l'objectif principal de la série d'articles est de travailler directement avec la base de données, donc si vous le souhaitez, vous pouvez concevoir un programme, etc., vous pouvez le faire vous-même. Structure actuelle du projet : En plus des référentiels et des modèles, nous avons également créé une classe StockExchangeDB
pour la gestion générale de notre émulation. A ce stade, nous gérons les référentiels (dans les prochaines parties, nous passerons aux services). Nous les déclarons et commençons à créer des tables :
package sql.demo;
import org.h2.tools.DeleteDbFiles;
import sql.demo.repository.*;
import java.sql.*;
public class StockExchangeDB {
// Блок объявления констант
public static final String DB_DIR = "c:/JavaPrj/SQLDemo/db";
public static final String DB_FILE = "stockExchange";
public static final String DB_URL = "jdbc:h2:/" + DB_DIR + "/" + DB_FILE;
public static final String DB_Driver = "org.h2.Driver";
// Таблицы СУБД
Traiders traiders;
ShareRates shareRates;
Shares shares;
TraiderShareActions traiderShareActions;
// Получить новое соединение с БД
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(DB_URL);
}
// Инициализация
public StockExchangeDB(boolean renew) throws SQLException, ClassNotFoundException {
if (renew)
DeleteDbFiles.execute(DB_DIR, DB_FILE, false);
Class.forName(DB_Driver);
// Инициализируем таблицы
traiders = new Traiders();
shares = new Shares();
shareRates = new ShareRates();
traiderShareActions = new TraiderShareActions();
}
// Инициализация по умолчанию, без удаления file БД
public StockExchangeDB() throws SQLException, ClassNotFoundException {
this(false);
}
// Creation всех таблиц и внешних ключей
public void createTablesAndForeignKeys() throws SQLException {
shares.createTable();
shareRates.createTable();
traiders.createTable();
traiderShareActions.createTable();
// Creation ограничений на поля таблиц
traiderShareActions.createExtraConstraints();
shares.createExtraConstraints();
// Creation внешних ключей (связи между tableми)
shareRates.createForeignKeys();
traiderShareActions.createForeignKeys();
}
public static void main(String[] args) {
try{
StockExchangeDB stockExchangeDB = new StockExchangeDB(true);
stockExchangeDB.createTablesAndForeignKeys();
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Ошибка SQL !");
} catch (ClassNotFoundException e) {
System.out.println("JDBC драйвер для СУБД не найден!");
}
}
}
Résultat de l'exécution :
Résumé
Dans les deuxième et troisième parties de l'article, nous avons appris :- Types de données SQL.
- Tableaux de base de données.
- Conception d'une base de données : structures de tables et relations entre elles.
- Langage de requête SQL en termes de création de tables de base de données, de définition de restrictions sur les champs et de relations entre les tables.
- En savoir plus sur l'interaction avec JDBC.
- Architecture modèle/référentiel/service à trois niveaux (trois couches) d'une application de traitement de données.
GO TO FULL VERSION