JavaRush /Blog Java /Random-FR /JDBC ou là où tout commence
Viacheslav
Niveau 3

JDBC ou là où tout commence

Publié dans le groupe Random-FR
Dans le monde moderne, il n’existe aucun moyen sans stockage de données. Et l'histoire du travail avec les bases de données a commencé il y a très longtemps, avec l'avènement de JDBC. Je propose de rappeler quelque chose dont aucun framework moderne construit sur JDBC ne peut se passer. De plus, même lorsque vous travaillez avec eux, vous pourriez parfois avoir besoin de l’opportunité de « retourner à vos racines ». J'espère que cette revue vous aidera en tant qu'introduction ou vous rafraîchira la mémoire.
JDBC ou là où tout commence - 1

Introduction

L'un des principaux objectifs d'un langage de programmation est le stockage et le traitement des informations. Pour mieux comprendre le fonctionnement du stockage de données, cela vaut la peine de s’attarder un peu sur la théorie et l’architecture des applications. Par exemple, vous pouvez lire la littérature, à savoir le livre « Software Architect's Handbook : Become a success software architect by Implementing Effective Arch... » de Joseph Ingeno. Comme indiqué, il existe un certain niveau de données ou « couche de données ». Il comprend un emplacement pour stocker les données (par exemple, une base de données SQL) et des outils pour travailler avec un magasin de données (par exemple, JDBC, qui sera discuté). Il existe également un article sur le site Web de Microsoft : « Conception d'une couche de persistance d'infrastructure », qui décrit la solution architecturale consistant à séparer une couche supplémentaire du niveau de données - la couche de persistance. Dans ce cas, le niveau de données est le niveau de stockage des données elles-mêmes, tandis que la couche de persistance est un certain niveau d'abstraction permettant de travailler avec les données du stockage du niveau du niveau de données. La couche de persistance peut inclure le modèle « DAO » ou divers ORM. Mais ORM est un sujet pour une autre discussion. Comme vous l'avez peut-être déjà compris, le niveau Données est apparu en premier. Depuis l'époque du JDK 1.1, JDBC (Java DataBase Connectivity - connexion aux bases de données en Java) est apparu dans le monde Java. Il s'agit d'un standard d'interaction des applications Java avec divers SGBD, implémenté sous la forme de packages java.sql et javax.sql inclus dans Java SE :
JDBC ou là où tout commence - 2
Ce standard est décrit par la spécification « JSR 221 JDBC 4.1 API ». Cette spécification nous indique que l'API JDBC fournit un accès programmatique aux bases de données relationnelles à partir de programmes écrits en Java. Il indique également que l'API JDBC fait partie de la plateforme Java et est donc incluse dans Java SE et Java EE. L'API JDBC est fournie dans deux packages : java.sql et javax.sql. Apprenons à les connaître alors.
JDBC ou là où tout commence - 3

Début des travaux

Pour comprendre ce qu'est l'API JDBC en général, nous avons besoin d'une application Java. Il est plus pratique d'utiliser l'un des systèmes d'assemblage du projet. Par exemple, utilisons Gradle . Vous pouvez en savoir plus sur Gradle dans une courte revue : " Une brève introduction à Gradle ". Commençons par initialiser un nouveau projet Gradle. Étant donné que la fonctionnalité Gradle est implémentée via des plugins, nous devons utiliser « Gradle Build Init Plugin » pour l'initialisation :
gradle init --type java-application
Après cela, ouvrons le script de construction - le fichier build.gradle , qui décrit notre projet et comment l'utiliser. Nous nous intéressons au bloc " dépendances ", où les dépendances sont décrites - c'est-à-dire ces bibliothèques/frameworks/api, sans lesquels nous ne pouvons pas travailler et dont nous dépendons. Par défaut, nous verrons quelque chose comme :
dependencies {
    // This dependency is found on compile classpath of this component and consumers.
    implementation 'com.google.guava:guava:26.0-jre'
    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}
Pourquoi voyons-nous cela ici ? Ce sont les dépendances de notre projet que Gradle a automatiquement générées pour nous lors de la création du projet. Et aussi parce que guava est une bibliothèque distincte qui n'est pas incluse avec Java SE. JUnit n'est pas non plus inclus avec Java SE. Mais nous avons JDBC prêt à l'emploi, c'est-à-dire qu'il fait partie de Java SE. Il s'avère que nous avons JDBC. Super. De quoi d'autre avons nous besoin? Il existe un diagramme si merveilleux :
JDBC ou là où tout commence - 4
Comme on peut le constater, et c'est logique, la base de données est un composant externe qui n'est pas natif à Java SE. Cela s'explique simplement : il existe un grand nombre de bases de données et vous pouvez travailler avec n'importe laquelle d'entre elles. Par exemple, il existe PostgreSQL, Oracle, MySQL, H2. Chacune de ces bases de données est fournie par une société distincte appelée fournisseurs de bases de données. Chaque base de données est écrite dans son propre langage de programmation (pas nécessairement Java). Afin de pouvoir travailler avec la base de données à partir d'une application Java, le fournisseur de base de données écrit un pilote spécial, qui est son propre adaptateur d'image. Ces bases de données compatibles JDBC (c'est-à-dire celles qui disposent d'un pilote JDBC) sont également appelées « base de données compatible JDBC ». Ici, nous pouvons faire une analogie avec les appareils informatiques. Par exemple, dans un bloc-notes, il y a un bouton "Imprimer". Chaque fois que vous appuyez dessus, le programme indique au système d'exploitation que l'application bloc-notes souhaite imprimer. Et vous avez une imprimante. Pour apprendre à votre système d'exploitation à communiquer uniformément avec une imprimante Canon ou HP, vous aurez besoin de différents pilotes. Mais pour vous, en tant qu’utilisateur, rien ne changera. Vous appuyerez toujours sur le même bouton. Idem avec JDBC. Vous exécutez le même code, c'est juste que différentes bases de données peuvent fonctionner sous le capot. Je pense que c'est une approche très claire. Chacun de ces pilotes JDBC est une sorte d'artefact, de bibliothèque ou de fichier jar. C'est la dépendance de notre projet. Par exemple, nous pouvons sélectionner la base de données " H2 Database " et ensuite nous devons ajouter une dépendance comme celle-ci :
dependencies {
    implementation 'com.h2database:h2:1.4.197'
Comment trouver une dépendance et comment la décrire est indiqué sur les sites officiels du fournisseur de base de données ou sur « Maven Central ». Le pilote JDBC n'est pas une base de données, comme vous l'avez compris. Mais il n'en est qu'un guide. Mais il existe des « bases de données en mémoire ». Ce sont des bases de données qui existent en mémoire pendant toute la durée de vie de votre application. En règle générale, cela est souvent utilisé à des fins de test ou de formation. Cela vous permet d'éviter d'installer un serveur de base de données distinct sur la machine. Ce qui nous convient très bien pour nous familiariser avec JDBC. Notre bac à sable est donc prêt et nous commençons.
JDBC ou là où tout commence - 5

Connexion

Nous avons donc un pilote JDBC, nous avons une API JDBC. Comme nous nous en souvenons, JDBC signifie Java DataBase Connectivity. Par conséquent, tout commence par la connectivité – la capacité d’établir une connexion. Et la connexion est la connexion. Revenons au texte de la spécification JDBC et regardons la table des matières. Dans le chapitre « CHAPITRE 4 Présentation » (aperçu) nous passons à la section « 4.1 Établissement d'une connexion » (établissement d'une connexion) il est dit qu'il existe deux manières de se connecter à la base de données :
  • Via DriverManager
  • Via la source de données
Parlons de DriverManager. Comme indiqué, DriverManager vous permet de vous connecter à la base de données à l'URL spécifiée et charge également les pilotes JDBC qu'il a trouvés dans CLASSPATH (et avant, avant JDBC 4.0, vous deviez charger vous-même la classe de pilote). Il existe un chapitre distinct « CHAPITRE 9 Connexions » sur la connexion à la base de données. Nous sommes intéressés par la façon d'obtenir une connexion via DriverManager, nous sommes donc intéressés par la section "9.3 La classe DriverManager". Il indique comment nous pouvons accéder à la base de données :
Connection con = DriverManager.getConnection(url, user, passwd);
Les paramètres peuvent être extraits du site Internet de la base de données que nous avons choisie. Dans notre cas, il s'agit de H2 - " H2 Cheat Sheet ". Passons à la classe AppTest préparée par Gradle. Il contient des tests JUnit. Un test JUnit est une méthode marquée d'une annotation @Test. Les tests unitaires ne font pas l'objet de cette revue, nous nous limiterons donc simplement à comprendre qu'il s'agit de méthodes décrites d'une certaine manière, dont le but est de tester quelque chose. Selon la spécification JDBC et le site H2, nous vérifierons que nous avons reçu une connexion à la base de données. Écrivons une méthode pour obtenir une connexion :
private Connection getNewConnection() throws SQLException {
	String url = "jdbc:h2:mem:test";
	String user = "sa";
	String passwd = "sa";
	return DriverManager.getConnection(url, user, passwd);
}
Écrivons maintenant un test pour cette méthode qui vérifiera que la connexion est bien établie :
@Test
public void shouldGetJdbcConnection() throws SQLException {
	try(Connection connection = getNewConnection()) {
		assertTrue(connection.isValid(1));
		assertFalse(connection.isClosed());
	}
}
Ce test, une fois exécuté, vérifiera que la connexion résultante est valide (créée correctement) et qu'elle n'est pas fermée. En utilisant try-with-resources, nous libérerons les ressources une fois que nous n'en aurons plus besoin. Cela nous protégera des connexions affaiblies et des fuites de mémoire. Étant donné que toute action avec la base de données nécessite une connexion, fournissons les méthodes de test restantes marquées @Test avec une connexion au début du test, que nous publierons après le test. Pour ce faire, nous avons besoin de deux annotations : @Before et @After Ajoutons un nouveau champ à la classe AppTest qui stockera la connexion JDBC pour les tests :
private static Connection connection;
Et ajoutons de nouvelles méthodes :
@Before
public void init() throws SQLException {
	connection = getNewConnection();
}
@After
public void close() throws SQLException {
	connection.close();
}
Désormais, toute méthode de test est garantie d'avoir une connexion JDBC et n'a pas besoin de la créer elle-même à chaque fois.
JDBC ou là où tout commence - 6

Déclarations

Ensuite, nous nous intéressons aux déclarations ou expressions. Elles sont décrites dans la documentation au chapitre « CHAPITRE 13 Déclarations ». Premièrement, il indique qu'il existe plusieurs types ou types de déclarations :
  • Instruction : expression SQL qui ne contient aucun paramètre
  • PreparedStatement : instruction SQL préparée contenant les paramètres d'entrée
  • CallableStatement : expression SQL avec la possibilité d'obtenir une valeur de retour à partir des procédures stockées SQL.
Ainsi, ayant une connexion, nous pouvons exécuter une requête dans le cadre de cette connexion. Il est donc logique que nous obtenions initialement une instance de l'expression SQL à partir de Connection. Vous devez commencer par créer un tableau. Décrivons la demande de création de table comme une variable String. Comment faire? Utilisons un tutoriel comme " sqltutorial.org ", " sqlbolt.com ", " postgresqltutorial.com ", " codecademy.com ". Utilisons, par exemple, un exemple du cours SQL sur khanacademy.org . Ajoutons une méthode pour exécuter une expression dans la base de données :
private int executeUpdate(String query) throws SQLException {
	Statement statement = connection.createStatement();
	// Для Insert, Update, Delete
	int result = statement.executeUpdate(query);
	return result;
}
Ajoutons une méthode pour créer une table de test en utilisant la méthode précédente :
private void createCustomerTable() throws SQLException {
	String customerTableQuery = "CREATE TABLE customers " +
                "(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)";
	String customerEntryQuery = "INSERT INTO customers " +
                "VALUES (73, 'Brian', 33)";
	executeUpdate(customerTableQuery);
	executeUpdate(customerEntryQuery);
}
Testons maintenant ceci :
@Test
public void shouldCreateCustomerTable() throws SQLException {
	createCustomerTable();
	connection.createStatement().execute("SELECT * FROM customers");
}
Exécutons maintenant la requête, et même avec un paramètre :
@Test
public void shouldSelectData() throws SQLException {
 	createCustomerTable();
 	String query = "SELECT * FROM customers WHERE name = ?";
	PreparedStatement statement = connection.prepareStatement(query);
	statement.setString(1, "Brian");
	boolean hasResult = statement.execute();
	assertTrue(hasResult);
}
JDBC ne prend pas en charge les paramètres nommés pour PreparedStatement, donc les paramètres eux-mêmes sont spécifiés par des questions, et en spécifiant la valeur, nous indiquons l'index de la question (à partir de 1 et non de zéro). Lors du dernier test, nous avons reçu vrai pour indiquer s'il y avait un résultat. Mais comment le résultat de la requête est-il représenté dans l'API JDBC ? Et il est présenté comme un ResultSet.
JDBC ou là où tout commence - 7

Ensemble de résultats

Le concept d'un ResultSet est décrit dans la spécification de l'API JDBC au chapitre "CHAPITRE 15 Result Sets". Tout d'abord, il indique que ResultSet fournit des méthodes pour récupérer et manipuler les résultats des requêtes exécutées. Autrement dit, si la méthode d'exécution nous renvoie true, nous pouvons alors obtenir un ResultSet. Déplaçons l'appel à la méthode createCustomerTable() vers la méthode init, qui est marquée comme @Before. Finalisons maintenant notre test ShouldSelectData :
@Test
public void shouldSelectData() throws SQLException {
	String query = "SELECT * FROM customers WHERE name = ?";
	PreparedStatement statement = connection.prepareStatement(query);
	statement.setString(1, "Brian");
	boolean hasResult = statement.execute();
	assertTrue(hasResult);
	// Обработаем результат
	ResultSet resultSet = statement.getResultSet();
	resultSet.next();
	int age = resultSet.getInt("age");
	assertEquals(33, age);
}
Il convient de noter ici qu’il existe ensuite une méthode qui déplace ce que l’on appelle le « curseur ». Le curseur dans ResultSet pointe vers une ligne. Ainsi, pour lire une ligne, vous devez placer ce même curseur dessus. Lorsque le curseur est déplacé, la méthode de déplacement du curseur renvoie vrai si le curseur est valide (correct, correct), c'est-à-dire qu'il pointe vers des données. S'il renvoie false, cela signifie qu'il n'y a pas de données, c'est-à-dire que le curseur ne pointe pas vers les données. Si nous essayons d'obtenir des données avec un curseur invalide, nous obtiendrons l'erreur : Aucune donnée n'est disponible. Il est également intéressant de noter que grâce à ResultSet, vous pouvez mettre à jour ou même insérer des lignes :
@Test
public void shouldInsertInResultSet() throws SQLException {
	Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
	ResultSet resultSet = statement.executeQuery("SELECT * FROM customers");
	resultSet.moveToInsertRow();
	resultSet.updateLong("id", 3L);
	resultSet.updateString("name", "John");
	resultSet.updateInt("age", 18);
	resultSet.insertRow();
	resultSet.moveToCurrentRow();
}

Ensemble de lignes

En plus de ResultSet, JDBC introduit le concept de RowSet. Vous pouvez en savoir plus ici : " Bases de JDBC : utilisation des objets RowSet ». Il existe diverses variantes d'utilisation. Par exemple, le cas le plus simple pourrait ressembler à ceci :
@Test
public void shouldUseRowSet() throws SQLException {
 	JdbcRowSet jdbcRs = new JdbcRowSetImpl(connection);
 	jdbcRs.setCommand("SELECT * FROM customers");
	jdbcRs.execute();
	jdbcRs.next();
	String name = jdbcRs.getString("name");
	assertEquals("Brian", name);
}
Comme vous pouvez le voir, RowSet est similaire à une symbiose entre une instruction (nous avons spécifié une commande à travers elle) et une commande exécutée. Grâce à lui, nous contrôlons le curseur (en appelant la méthode suivante) et en obtenons des données. Non seulement cette approche est intéressante, mais aussi ses mises en œuvre possibles. Par exemple, CachedRowSet. Il est « déconnecté » (c'est-à-dire qu'il n'utilise pas de connexion persistante à la base de données) et nécessite une synchronisation explicite avec la base de données :
CachedRowSet jdbcRsCached = new CachedRowSetImpl();
jdbcRsCached.acceptChanges(connection);
Vous pouvez en savoir plus dans le tutoriel sur le site d'Oracle : " Using CachedRowSetObjects ".
JDBC ou là où tout commence - 8

Métadonnées

En plus des requêtes, une connexion à la base de données (c'est-à-dire une instance de la classe Connection) permet d'accéder aux métadonnées - des données sur la façon dont notre base de données est configurée et organisée. Mais d’abord, mentionnons quelques points clés : L’URL de connexion à notre base de données : « jdbc:h2:mem:test ». test est le nom de notre base de données. Pour l'API JDBC, il s'agit d'un répertoire. Et le nom sera en majuscule, c'est-à-dire TEST. Le schéma par défaut pour H2 est PUBLIC. Maintenant, écrivons un test qui affiche toutes les tables utilisateur. Pourquoi du sur mesure ? Parce que les bases de données contiennent non seulement des tables utilisateur (celles que nous avons nous-mêmes créées à l'aide d'expressions de création de table), mais également des tables système. Ils sont nécessaires pour stocker des informations système sur la structure de la base de données. Chaque base de données peut stocker ces tables système différemment. Par exemple, en H2 ils sont stockés dans le schéma " INFORMATION_SCHEMA ". Il est intéressant de noter que INFORMATION SCHEMA est une approche courante, mais Oracle a emprunté une voie différente. Vous pouvez en savoir plus ici : " INFORMATION_SCHEMA et Oracle ". Écrivons un test qui reçoit des métadonnées sur les tables utilisateur :
@Test
public void shoudGetMetadata() throws SQLException {
	// У нас URL = "jdbc:h2:mem:test", где test - название БД
	// Название БД = catalog
	DatabaseMetaData metaData = connection.getMetaData();
	ResultSet result = metaData.getTables("TEST", "PUBLIC", "%", null);
	List<String> tables = new ArrayList<>();
	while(result.next()) {
		tables.add(result.getString(2) + "." + result.getString(3));
	}
	assertTrue(tables.contains("PUBLIC.CUSTOMERS"));
}
JDBC ou là où tout commence - 9

Pool de connexions

Le pool de connexions dans la spécification JDBC comporte une section intitulée « Chapitre 11 Pooling de connexions ». C’est également la principale justification de la nécessité d’un pool de connexions. Chaque Coonection est une connexion physique à la base de données. Sa création et sa fermeture sont un travail assez « coûteux ». JDBC fournit uniquement une API de regroupement de connexions. Le choix de la mise en œuvre nous appartient donc. Par exemple, ces implémentations incluent HikariCP . En conséquence, nous devrons ajouter un pool à la dépendance de notre projet :
dependencies {
    implementation 'com.h2database:h2:1.4.197'
    implementation 'com.zaxxer:HikariCP:3.3.1'
    testImplementation 'junit:junit:4.12'
}
Maintenant, nous devons utiliser ce pool d'une manière ou d'une autre. Pour ce faire, vous devez initialiser la source de données, également appelée Datasource :
private DataSource getDatasource() {
	HikariConfig config = new HikariConfig();
	config.setUsername("sa");
	config.setPassword("sa");
	config.setJdbcUrl("jdbc:h2:mem:test");
	DataSource ds = new HikariDataSource(config);
	return ds;
}
Et écrivons un test pour recevoir une connexion du pool :
@Test
public void shouldGetConnectionFromDataSource() throws SQLException {
	DataSource datasource = getDatasource();
	try (Connection con = datasource.getConnection()) {
		assertTrue(con.isValid(1));
	}
}
JDBC ou là où tout commence - 10

Transactions

L'une des choses les plus intéressantes à propos de JDBC concerne les transactions. Dans la spécification JDBC, le chapitre "CHAPITRE 10 Transactions" leur est attribué. Tout d’abord, il convient de comprendre ce qu’est une transaction. Une transaction est un groupe d'opérations séquentielles logiquement combinées sur des données, traitées ou annulées dans leur ensemble. Quand une transaction démarre-t-elle lors de l’utilisation de JDBC ? Comme l'indique la spécification, cela est géré directement par le pilote JDBC. Mais généralement, une nouvelle transaction commence lorsque l'instruction SQL actuelle nécessite une transaction et que la transaction n'a pas encore été créée. Quand se termine la transaction ? Ceci est contrôlé par l’attribut auto-commit. Si la validation automatique est activée, la transaction sera terminée une fois l'instruction SQL « terminée ». La signification de « terminé » dépend du type d'expression SQL :
  • Langage de manipulation de données, également appelé DML (Insert, Update, Delete)
    La transaction est terminée dès que l'action est terminée
  • Instructions Select
    La transaction est terminée lorsque le ResultSet est fermé ( ResultSet#close )
  • CallableStatement et expressions qui renvoient plusieurs résultats
    Lorsque tous les ResultSets associés ont été fermés et que toutes les sorties ont été reçues (y compris le nombre de mises à jour)
C'est exactement ainsi que se comporte l'API JDBC. Comme d'habitude, écrivons un test pour cela :
@Test
public void shouldCommitTransaction() throws SQLException {
	connection.setAutoCommit(false);
	String query = "INSERT INTO customers VALUES (1, 'Max', 20)";
	connection.createStatement().executeUpdate(query);
	connection.commit();
	Statement statement = connection.createStatement();
 	statement.execute("SELECT * FROM customers");
	ResultSet resultSet = statement.getResultSet();
	int count = 0;
	while(resultSet.next()) {
		count++;
	}
	assertEquals(2, count);
}
C'est simple. Mais cela est vrai tant que nous n’avons qu’une seule transaction. Que faire quand il y en a plusieurs ? Ils doivent être isolés les uns des autres. Parlons donc des niveaux d'isolement des transactions et de la manière dont JDBC les gère.
JDBC ou là où tout commence - 11

Niveaux d'isolation

Ouvrons la sous-section « 10.2 Niveaux d'isolation des transactions » de la spécification JDBC. Ici, avant d'aller plus loin, je voudrais me souvenir d'une chose telle que ACID. ACID décrit les exigences d'un système transactionnel.
  • Atomicité :
    Aucune transaction ne sera partiellement validée dans le système. Soit toutes ses sous-opérations seront réalisées, soit aucune ne sera réalisée.
  • Cohérence :
    chaque transaction réussie, par définition, n'enregistre que des résultats valides.
  • Isolation :
    pendant qu'une transaction est en cours d'exécution, les transactions simultanées ne devraient pas affecter son résultat.
  • Durabilité :
    si une transaction est terminée avec succès, les modifications qui y ont été apportées ne seront pas annulées en cas d'échec.
Lorsque nous parlons de niveaux d’isolement des transactions, nous parlons de l’exigence « d’isolement ». L'isolement est une exigence coûteuse, c'est pourquoi dans les bases de données réelles, il existe des modes qui n'isolent pas complètement une transaction (niveaux d'isolement de lecture répétable et inférieurs). Wikipédia propose une excellente explication des problèmes qui peuvent survenir lors de l'utilisation de transactions. Cela vaut la peine d'en lire davantage ici : « Problèmes d'accès parallèle à l'aide de transactions ». Avant d'écrire notre test, modifions légèrement notre Gradle Build Script : ajoutons un bloc avec des propriétés, c'est-à-dire avec les paramètres de notre projet :
ext {
    h2Version = '1.3.176' // 1.4.177
    hikariVersion = '3.3.1'
    junitVersion = '4.12'
}
Ensuite, nous l'utilisons dans les versions :
dependencies {
    implementation "com.h2database:h2:${h2Version}"
    implementation "com.zaxxer:HikariCP:${hikariVersion}"
    testImplementation "junit:junit:${junitVersion}"
}
Vous avez peut-être remarqué que la version h2 est devenue inférieure. Nous verrons pourquoi plus tard. Alors, comment appliquez-vous les niveaux d’isolement ? Regardons tout de suite un petit exemple pratique :
@Test
public void shouldGetReadUncommited() throws SQLException {
	Connection first = getNewConnection();
	assertTrue(first.getMetaData().supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED));
	first.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
	first.setAutoCommit(false);
	// Транзакиця на подключение. Поэтому первая транзакция с ReadUncommited вносит изменения
	String insertQuery = "INSERT INTO customers VALUES (5, 'Max', 15)";
	first.createStatement().executeUpdate(insertQuery);
	// Вторая транзакция пытается их увидеть
	int rowCount = 0;
	JdbcRowSet jdbcRs = new JdbcRowSetImpl(getNewConnection());
	jdbcRs.setCommand("SELECT * FROM customers");
	jdbcRs.execute();
	while (jdbcRs.next()) {
		rowCount++;
	}
	assertEquals(2, rowCount);
}
Il est intéressant de noter que ce test peut échouer sur un fournisseur qui ne prend pas en charge TRANSACTION_READ_UNCOMMITTED (par exemple, sqlite ou HSQL). Et le niveau de transaction peut tout simplement ne pas fonctionner. Vous vous souvenez que nous avons indiqué la version du pilote H2 Database ? Si nous l'élevons à h2Version = '1.4.177' et plus, alors READ UNCOMMITTED cessera de fonctionner, même si nous n'avons pas modifié le code. Cela prouve une fois de plus que le choix du fournisseur et de la version du pilote ne se limite pas à des lettres, il déterminera en fait la manière dont vos demandes seront exécutées. Vous pouvez découvrir comment résoudre ce problème dans la version 1.4.177 et comment cela ne fonctionne pas dans les versions supérieures ici : " Prise en charge du niveau d'isolation READ UNCOMMITTED en mode MVStore ".
JDBC ou là où tout commence - 12

Conclusion

Comme nous pouvons le constater, JDBC est un outil puissant entre les mains de Java pour travailler avec des bases de données. J'espère que cette courte revue vous aidera à vous donner un point de départ ou à vous rafraîchir la mémoire. Bon, pour le goûter, quelques matériels supplémentaires : #Viacheslav
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION