Serveur d'impression

Avancée – Bien choisir son serveur d impression

Par Titanfall , le 4 mai 2019 - 63 minutes de lecture

Ensembles de résultats
Gros objets
Tables liées
Caractéristiques spatiales
Requêtes récursives
Vues pouvant être mises à jour
Isolement de transaction
Contrôle de la simultanéité multi-version (MVCC)
Regroupement / Haute disponibilité
Engagement en deux phases
Compatibilité
Mots-clés / mots réservés
Conformité aux normes
Exécuter en tant que service Windows
Pilote ODBC
Utiliser H2 dans Microsoft .NET
ACIDE
Problèmes de durabilité
Utiliser l'outil de récupération
Protocoles de verrouillage de fichiers
Utilisation de mots de passe
Mot de passe hachage
Protection contre l'injection SQL
Protection contre l'accès à distance
Limiter le chargement et l'utilisation de la classe
Protocoles de sécurité
Connexions TLS
Identificateurs universellement uniques (UUID)
Paramètres lus à partir des propriétés système
Définition de l'adresse de liaison au serveur
Système de fichiers enfichable
Système de fichiers fractionné
Mise à niveau de la base de données
Sérialisation des objets Java
API de gestion des types de données personnalisés
Limites et Limites
Glossaire et liens

Sommaire

Ensembles de résultats

Instructions qui renvoient un jeu de résultats

Les instructions suivantes renvoient un jeu de résultats: SÉLECTIONNER, EXPLIQUER, APPELER, SCRIPT, MONTRER, AIDE. Toutes les autres instructions renvoient un nombre de mises à jour.

Limiter le nombre de lignes

Avant que le résultat ne soit renvoyé à l'application, toutes les lignes sont lues par la base de données. Les curseurs côté serveur ne sont pas pris en charge pour le moment. Si seules les premières lignes sont intéressantes pour l'application, la taille du jeu de résultats doit être limitée pour améliorer les performances. Cela peut être fait en utilisant FETCH dans une requête (exemple: SÉLECTIONNEZ * À PARTIR DU TEST FETCH PREMIER 100 RANGÉES SEULEMENT), ou en utilisant Statement.setMaxRows (max).

Grands ensembles de résultats et tri externe

Pour un grand ensemble de résultats, le résultat est mis en mémoire tampon sur le disque. Le seuil peut être défini à l'aide de l'instruction SET MAX_MEMORY_ROWS. Si COMMANDÉ PAR est utilisé, le tri est effectué en utilisant un algorithme de tri externe. Dans ce cas, chaque bloc de lignes est trié à l'aide d'un tri rapide, puis écrit sur le disque; lors de la lecture des données, les blocs sont fusionnés.

Gros objets

Stockage et lecture de gros objets

S'il est possible que les objets n'entrent pas dans la mémoire, vous devez utiliser le type de données CLOB (pour les données textuelles) ou BLOB (pour les données binaires). Pour ces types de données, les objets ne sont pas entièrement lus en mémoire, à l'aide de flux. Pour stocker un BLOB, utilisez PreparedStatement.setBinaryStream. Pour stocker un CLOB, utilisez PreparedStatement.setCharacterStream. Pour lire un BLOB, utilisez ResultSet.getBinaryStream, et pour lire un CLOB, utilisez ResultSet.getCharacterStream. Lorsque vous utilisez le mode client / serveur, les données BLOB et CLOB volumineuses sont stockées dans un fichier temporaire côté client.

Quand utiliser CLOB / BLOB

Par défaut, cette base de données stocke des objets LOB volumineux (CLOB et BLOB) séparément des données de la table principale. Les petits objets LOB sont stockés sur place, le seuil peut être défini à l'aide de MAX_LENGTH_INPLACE_LOB, mais l'utilisation de CLOB / BLOB est toujours une surcharge. C'est pourquoi BLOB et CLOB ne doivent jamais être utilisés pour des colonnes dont la taille maximale est inférieure à 200 octets. Le meilleur seuil dépend du cas d'utilisation. la lecture d'objets sur place est plus rapide que la lecture de fichiers distincts, mais ralentit les performances des opérations n'impliquant pas cette colonne.

Compression de gros objets

La fonctionnalité suivante est uniquement disponible pour le moteur de stockage PageStore. Pour le moteur MVStore (valeur par défaut pour la version H2 1.4.x), ajoutez ; COMPRESS = TRUE à l'URL de la base de données à la place. Les valeurs CLOB et BLOB peuvent être compressées à l'aide de SET COMPRESS_LOB. L'algorithme LZF est plus rapide mais nécessite plus d'espace disque. Par défaut, la compression est désactivée, ce qui accélère généralement les opérations d'écriture. Si vous stockez de nombreuses valeurs compressibles importantes telles que XML, HTML, texte et fichiers binaires non compressés, la compression peut économiser beaucoup d'espace disque (parfois plus de 50%) et les opérations de lecture peuvent même être plus rapides.

Tables liées

Cette base de données prend en charge les tables liées, ce qui signifie que les tables n'existent pas dans la base de données actuelle mais sont simplement des liens vers une autre base de données. Pour créer un tel lien, utilisez le CRÉER UNE TABLE LIÉE déclaration:




CREATE LINKED TABLE LINK ('org.postgresql.Driver', 'jdbc: postgresql: test', 'sa', 'sa', 'TEST');

Vous pouvez ensuite accéder à la table de la manière habituelle. Chaque fois que vous accédez à la table liée, la base de données émet des requêtes spécifiques sur JDBC. En utilisant l'exemple ci-dessus, si vous émettez la requête SELECT * FROM LINK WHERE ID = 1, la requête suivante est alors exécutée sur la base de données PostgreSQL: SELECT * FROM TEST WHERE ID =?. La même chose se produit pour les instructions insert et update. Seules des instructions simples sont exécutées sur la base de données cible, ce qui signifie qu'aucune jointure (les requêtes contenant des jointures sont converties en requêtes simples). Les instructions préparées sont utilisées dans la mesure du possible.

Pour afficher les instructions exécutées sur la table cible, définissez le niveau de trace sur 3.

Si plusieurs tables liées pointent vers la même base de données (en utilisant la même URL de base de données), la connexion est partagée. Pour le désactiver, définissez la propriété système. h2.shareLinkedConnections = false.

L'instruction CREATE LINKED TABLE prend en charge un paramètre de nom de schéma facultatif.

Les éléments suivants ne sont pas pris en charge car ils peuvent entraîner un blocage: création d'une table liée dans la même base de données et création d'une table liée dans une autre base de données à l'aide du mode serveur si l'autre base de données est ouverte sur le même serveur (utilisez plutôt le mode incorporé) ).

Les types de données non pris en charge dans H2 ne le sont pas non plus pour les tables liées, par exemple les types de données non signés si la valeur est en dehors de la plage du type signé. Dans ce cas, les colonnes doivent être converties en un type pris en charge.

Vues pouvant être mises à jour

Par défaut, les vues ne peuvent pas être mises à jour. Pour rendre une vue modifiable, utilisez un déclencheur "au lieu de" comme suit:




CREATE TRIGGER TRIGGER_NAME
AU LIEU DE L'INSERTION, MISE À JOUR, SUPPRIMER
ON VIEW_NAME
POUR CHAQUE RANG, APPELEZ "com.acme.TriggerClassName";

Mettez à jour la ou les tables de base dans le déclencheur selon les besoins. Pour plus de détails, voir l'exemple d'application org.h2.samples.UpdatableView.

Isolement de transaction

Veuillez noter que la plupart des instructions DDL (langage de définition de données), telles que "créer une table", valident la transaction en cours. Voir les commandes pour plus de détails.

L'isolation de transaction est fournie pour toutes les instructions DML (Data Manipulation Language).

Veuillez noter que le verrouillage de niveau de table du moteur de stockage MVStore par défaut n'est pas utilisé. Au lieu de cela, les lignes sont verrouillées pour la mise à jour et la lecture lue est utilisée dans tous les cas, à l'exception du niveau d'isolation de la transaction de lecture non validée explicitement sélectionné.

Cette base de données prend en charge les niveaux d'isolation de transaction suivants:

  • Lire commis
    C'est le niveau par défaut. Les verrous en lecture sont libérés immédiatement après l'exécution de l'instruction, mais les verrous en écriture sont conservés jusqu'à la validation de la transaction. Une plus grande simultanéité est possible avec ce niveau.
    Pour l'activer, exécutez l'instruction SQL SET LOCK_MODE 3
    ou ajouter ; LOCK_MODE = 3 à l'URL de la base de données: jdbc: h2: ~ / test; LOCK_MODE = 3
  • Sérialisable
    Les verrous en lecture et en écriture sont conservés jusqu'à la validation de la transaction. Pour l'activer, exécutez l'instruction SQL SET LOCK_MODE 1
    ou ajouter ; LOCK_MODE = 1 à l'URL de la base de données: jdbc: h2: ~ / test; LOCK_MODE = 1
  • Lire non engagé
    Ce niveau signifie que l'isolation de transaction est désactivée. Ce niveau n'est pas pris en charge par le moteur PageStore si le mode multi-thread est activé.
    Pour l'activer, exécutez l'instruction SQL SET LOCK_MODE 0
    ou ajouter ; LOCK_MODE = 0 à l'URL de la base de données: jdbc: h2: ~ / test; LOCK_MODE = 0

Lorsque vous utilisez le niveau d'isolation 'sérialisable', les lectures modifiées, les lectures non répétables et les lectures fantômes sont interdites.

  • Lectures sales
    Signifie qu'une connexion peut lire les modifications non validées effectuées par une autre connexion.
    Possible avec: lu non engagé
  • Lectures non répétables
    Une connexion lit une ligne, une autre connexion modifie une ligne et la valide, et la première connexion relit la même ligne et obtient le nouveau résultat.
    Possible avec: lecture non engagée, lecture validée
  • Lectures fantômes
    Une connexion lit un ensemble de lignes en utilisant une condition, une autre connexion insère une ligne qui tombe dans cette condition et est validée, puis la première connexion relit en utilisant la même condition et obtient la nouvelle ligne.
    Possible avec: lecture non engagée, lecture validée

Contrôle de la simultanéité multi-version (MVCC)

Avec le moteur MVStore par défaut, les opérations de suppression, d'insertion et de mise à jour n'émettent qu'un verrou partagé sur la table. Un verrou exclusif est toujours utilisé lors de l'ajout ou de la suppression de colonnes ou lors de la suppression de la table. Les connexions ne «voient» que les données validées et leurs propres modifications. Cela signifie que si la connexion A met à jour une ligne mais ne valide pas encore cette modification, la connexion B verra l'ancienne valeur. Ce n'est que lorsque la modification est validée que la nouvelle valeur est visible par d'autres connexions (lecture validée). Si plusieurs connexions tentent simultanément de verrouiller ou de mettre à jour la même ligne, la base de données attend jusqu'à ce qu'elle puisse appliquer la modification, mais au plus jusqu'à l'expiration du délai de verrouillage.

Verrouillage du niveau de la table (moteur de magasin de page)

Avec le moteur PageStore pour vous assurer que toutes les connexions ne voient que des données cohérentes, le verrouillage au niveau de la table est utilisé. Ce mécanisme ne permet pas une grande concurrence, mais est très rapide. Les verrous partagés et les verrous exclusifs sont pris en charge. Avant de lire une table, la base de données essaie d'ajouter un verrou partagé à la table (ceci n'est possible que s'il n'y a pas de verrou exclusif sur l'objet par une autre connexion). Si le verrou partagé est ajouté avec succès, la table peut être lue. Il est permis que d'autres connexions aient également un verrou partagé sur le même objet. Si une connexion veut écrire dans une table (mettre à jour ou supprimer une ligne), un verrou exclusif est requis. Pour obtenir le verrou exclusif, aucune autre connexion ne doit avoir de verrou sur l'objet. Une fois la connexion validée, tous les verrous sont libérés. Cette base de données conserve tous les verrous en mémoire. Lorsqu'un verrou est libéré et que plusieurs connexions l'attendent, l'une d'entre elles est sélectionnée au hasard.

Verrouillage du délai d'attente

Si une connexion ne peut pas verrouiller un objet, elle attend pendant un certain temps (délai de verrouillage). Pendant ce temps, nous espérons que la connexion qui maintient le verrou est validée et qu’il est alors possible d’obtenir le verrou. Si cela n'est pas possible car l'autre connexion ne libère pas le verrou pendant un certain temps, la connexion qui échoue obtiendra une exception de délai de verrouillage. Le délai de verrouillage peut être défini individuellement pour chaque connexion.

Regroupement / Haute disponibilité

Cette base de données prend en charge un mécanisme simple de clustering / haute disponibilité. L'architecture est la suivante: deux serveurs de base de données s'exécutent sur deux ordinateurs différents et une copie de la même base de données est disponible sur les deux ordinateurs. Si les deux serveurs s'exécutent, chaque opération de base de données est exécutée sur les deux ordinateurs. Si un serveur tombe en panne (panne d'alimentation, de matériel ou de réseau), l'autre serveur peut toujours continuer à fonctionner. À partir de ce moment, les opérations ne seront exécutées que sur un serveur jusqu'à la sauvegarde de l'autre.

Le clustering ne peut être utilisé qu'en mode serveur (le mode intégré ne prend pas en charge le clustering). Le cluster peut être recréé en utilisant le CreateCluster outil sans arrêter le serveur restant. Les applications qui sont toujours connectées sont automatiquement déconnectées, mais lors de l'ajout ; AUTO_RECONNECT = TRUE, ils vont s'en remettre.

Pour initialiser le cluster, procédez comme suit:

  • Créer une base de données
  • Utilisez le CreateCluster outil pour copier la base de données vers un autre emplacement et initialiser la mise en cluster. Ensuite, vous avez deux bases de données contenant les mêmes données.
  • Démarrer deux serveurs (un pour chaque copie de la base de données)
  • Vous êtes maintenant prêt à vous connecter aux bases de données avec la ou les applications clientes.

Utilisation de l'outil CreateCluster

Pour comprendre le fonctionnement du clustering, veuillez essayer l'exemple suivant. Dans cet exemple, les deux bases de données résident sur le même ordinateur, mais elles sont généralement situées sur des serveurs différents.

  • Créez deux répertoires: serveur1, serveur2. Chaque répertoire simulera un répertoire sur un ordinateur.
  • Démarrez un serveur TCP pointant vers le premier répertoire. Vous pouvez le faire en utilisant la ligne de commande:
    java org.h2.tools.Server
        -tcp -tcpPort 9101
        -baseDir server1
    
  • Démarrez un deuxième serveur TCP pointant vers le deuxième répertoire. Cela simulera un serveur fonctionnant sur un deuxième ordinateur (redondant). Vous pouvez le faire en utilisant la ligne de commande:
    java org.h2.tools.Server
        -tcp -tcpPort 9102
        -baseDir server2
    
  • Utilisez le CreateCluster outil pour initialiser le clustering. Cela créera automatiquement une nouvelle base de données vide si elle n'existe pas. Exécutez l'outil sur la ligne de commande:
    java org.h2.tools.CreateCluster
        -urlSource jdbc: h2: tcp: // localhost: 9101 / ~ / test
        -urlTarget jdbc: h2: tcp: // localhost: 9102 / ~ / test
        -user sa
        -serverList localhost: 9101, localhost: 9102
    
  • Vous pouvez maintenant vous connecter aux bases de données à l'aide d'une application ou de la console H2 à l'aide de l'URL JDBC. jdbc: h2: tcp: // localhost: 9101, localhost: 9102 / ~ / test
  • Si vous arrêtez un serveur (en tuant le processus), vous remarquerez que l'autre ordinateur continue de fonctionner et que, par conséquent, la base de données est toujours accessible.
  • Pour restaurer le cluster, vous devez d'abord supprimer la base de données qui a échoué, puis redémarrer le serveur arrêté et réexécuter le processus. CreateCluster outil.

Détecter les instances de cluster en cours d'exécution

Pour connaître les nœuds de cluster en cours d'exécution, exécutez l'instruction SQL suivante:




SÉLECTIONNER LA VALEUR DANS INFORMATION_SCHEMA.SETTINGS WHERE NAME = 'CLUSTER'

Si le résultat est '' (deux guillemets simples), le mode cluster est désactivé. Sinon, la liste des serveurs est renvoyée, entre guillemets simples. Exemple: 'serveur1: 9191, serveur2: 9191'.

Il est également possible d'obtenir la liste des serveurs à l'aide de Connection.getClientInfo ().

La liste de propriétés renvoyée de getClientInfo () contient un numServers propriété qui renvoie le nombre de serveurs figurant dans la liste de connexion. Pour obtenir les serveurs actuels, getClientInfo () a aussi des propriétés serveur0..serveurX, où serverX est le nombre de serveurs moins 1.

Exemple: pour obtenir le deuxième serveur dans la liste de connexion on utilise getClientInfo ('serveur1'). Remarque: le serveurX Cette propriété ne renvoie que les adresses IP et les ports et non les noms d’hôte.

Algorithme de clustering et limitations

Les requêtes en lecture seule ne sont exécutées que sur le premier nœud du cluster, mais toutes les autres instructions sont exécutées sur tous les nœuds. Actuellement, aucun équilibrage de charge n'est effectué pour éviter les problèmes de transactions. Les fonctions suivantes peuvent produire des résultats différents sur différents nœuds de cluster et doivent être exécutées avec précaution: UUID (), RANDOM_UUID (), SECURE_RAND (), SESSION_ID (), MEMORY_FREE (), MEMORY_USED (), CSVREAD (), CSVWRITE (), RAND (). [when not using a seed]. Ces fonctions ne doivent pas être utilisées directement pour modifier des instructions (par exemple INSÉRER, METTRE À JOUR, FUSIONNER). Cependant, ils peuvent être utilisés dans des instructions en lecture seule et le résultat peut ensuite être utilisé pour modifier des instructions. L'utilisation de colonnes d'auto-incrémentation et d'identité n'est actuellement pas prise en charge. Au lieu de cela, les valeurs de séquence doivent être demandées manuellement, puis utilisées pour insérer des données (à l'aide de deux instructions).

Lors de l'utilisation des modes de cluster, les ensembles de résultats sont lus entièrement en mémoire par le client, de sorte qu'il n'y a aucun problème si le serveur meurt qui a exécuté la requête. Les ensembles de résultats doivent tenir dans la mémoire côté client.

L'instruction SQL SET AUTOCOMMIT FALSE n'est pas pris en charge en mode cluster. Pour désactiver autocommit, la méthode Connection.setAutoCommit (false) doit être appelé.

Il est possible qu'une transaction d'une connexion supplante une transaction d'une autre connexion. Selon les opérations, cela peut donner des résultats différents, par exemple lors de l'incrémentation conditionnelle d'une valeur dans une ligne.

Engagement en deux phases

Le protocole de validation en deux phases est pris en charge. Le validation en deux phases fonctionne comme suit:

  • Autocommit doit être désactivé
  • Une transaction est lancée, par exemple en insérant une ligne
  • La transaction est marquée 'préparée' en exécutant l'instruction SQL PREPARE COMMIT transactionName
  • La transaction peut maintenant être validée ou annulée
  • Si un problème survient avant que la transaction soit validée ou annulée (par exemple, en raison d'un problème de réseau), la transaction est dans l'état "en attente".
  • Lors de la reconnexion à la base de données, les transactions en suspens peuvent être listées avec SELECT * FROM INFORMATION_SCHEMA.IN_DOUBT
  • Chaque transaction de cette liste doit maintenant être validée ou annulée en exécutant COMMIT TRANSACTION transactionName ou ROLLBACK TRANSACTION transactionName
  • La base de données doit être fermée et rouverte pour appliquer les modifications

Compatibilité

Cette base de données est (jusqu'à un certain point) compatible avec d'autres bases de données telles que HSQLDB, MySQL et PostgreSQL. Il y a certaines zones où H2 est incompatible.

Transaction Commit quand Autocommit est activé

À ce stade, ce moteur de base de données valide une transaction (si autocommit est activé) juste avant de renvoyer le résultat. Pour une requête, cela signifie que la transaction est validée avant même que l'application n'analyse l'ensemble de résultats et avant que celui-ci ne soit fermé. D'autres moteurs de base de données peuvent valider la transaction dans ce cas lorsque l'ensemble de résultats est fermé.

Mots-clés / mots réservés

Il existe une liste de mots-clés qui ne peuvent pas être utilisés comme identificateurs (noms de table, noms de colonne, etc.), à moins qu'ils ne soient entre guillemets (entourés de guillemets). Les jetons suivants sont des mots clés dans H2:

Mot-clé H2 SQL: 2011 SQL: 2008 SQL: 2003 SQL: 1999 SQL-92
TOUT + + + + + +
ET CS + + + + +
Tableau + + + + +
COMME CS + + + + +
ENTRE CS + + + NR +
TOUS LES DEUX CS + + + + +
CAS + + + + + +
VÉRIFIER + + + + + +
CONTRAINTE + + + + + +
TRAVERSER + + + + + +
DATE ACTUELLE + + + + + +
HEURE ACTUELLE + + + + + +
CURRENT_TIMESTAMP + + + + + +
UTILISATEUR ACTUEL + + + + + +
DISTINCT + + + + + +
SAUF + + + + + +
EXISTE + + + + NR +
FAUX + + + + + +
FETCH + + + + + +
FILTRE CS + + +
POUR + + + + + +
ÉTRANGER + + + + + +
DE + + + + + +
PLEIN + + + + + +
GROUPE + + + + + +
GROUPES CS +
AYANT + + + + + +
SI +
J'AIME CS
DANS CS + + + + +
INTERNE + + + + + +
COUPER + + + + + +
INTERSECTS +
INTERVALLE + + + + + +
EST + + + + + +
JOINDRE + + + + + +
DE PREMIER PLAN CS + + + + +
LA GAUCHE CS + + + + +
COMME + + + + + +
LIMITE + +
HEURE LOCALE + + + + +
LOCALTIMESTAMP + + + + +
MOINS +
NATUREL + + + + + +
NE PAS + + + + + +
NUL + + + + + +
DÉCALAGE + + +
SUR + + + + + +
OU CS + + + + +
ORDRE + + + + + +
PLUS DE CS + + +
CLOISON CS + + +
PRIMAIRE + + + + + +
QUALIFIER +
INTERVALLE CS + + +
REGEXP CS
DROITE CS + + + + +
RANGÉE + + + + +
_ROWID_ +
ROWNUM +
ROWS CS + + + + +
SÉLECTIONNER + + + + + +
SYSDATE CS
SYSTIME CS
SYSTIMESTAMP CS
TABLE + + + + + +
AUJOURD'HUI CS
HAUT CS
TRAILING CS + + + + +
VRAI + + + + + +
SYNDICAT + + + + + +
UNIQUE + + + + + +
VALEURS + + + + + +
+ + + + + +
LA FENÊTRE + + + +
AVEC + + + + + +

Certains mots-clés de H2 sont sensibles au contexte (CS). De tels mots-clés peuvent être utilisés comme identificateurs à certains endroits, mais ne peuvent l'être comme identificateurs à d'autres. La plupart des mots-clés de H2 sont également des mots réservés (+) ou non réservés (NR) dans SQL Standard. Les nouvelles versions de H2 peuvent comporter plus de mots-clés que les anciennes.

Conformité aux normes

Cette base de données tente d’être aussi conforme que possible aux normes. ANSI / ISO est la norme principale pour le langage SQL. Plusieurs versions font référence à la date de publication: SQL-92, SQL: 1999 et SQL: 2003. Malheureusement, la documentation standard n'est pas librement disponible. Un autre problème est que les fonctionnalités importantes ne sont pas normalisées. Chaque fois que cela est le cas, cette base de données essaie d'être compatible avec d'autres bases de données.

Jeux de caractères, codage de caractères et Unicode pris en charge

H2 utilise en interne Unicode et prend en charge tous les systèmes de codage de caractères et les jeux de caractères pris en charge par la machine virtuelle que vous utilisez.

Exécuter en tant que service Windows

À l'aide d'un adaptateur / encapsuleur natif, les applications Java peuvent être exécutées en tant que service Windows. Il existe différents outils pour le faire. Le wrapper de service Java de Tanuki Software, Inc. est inclus dans l’installation. Les fichiers de commandes sont fournis pour installer, démarrer, arrêter et désinstaller le service H2 Database Engine. Ce service contient le serveur TCP et l'application Web H2 Console. Les fichiers de commandes sont situés dans le répertoire h2 / service.

Le wrapper de service fourni avec H2 est une version 32 bits. Pour utiliser une version 64 bits de Windows (x64), vous devez utiliser une version 64 bits du wrapper, par exemple celle de Simon Krenger.

Lors de l'exécution de la base de données en tant que service, le chemin absolu doit être utilisé. En utilisant ~ dans la base de données, l'URL est problématique dans ce cas, car cela signifie d'utiliser le répertoire de base de l'utilisateur actuel. Le service peut s'exécuter sans ou avec le mauvais utilisateur, de sorte que les fichiers de base de données risquent de se retrouver dans un endroit inattendu.

Installer le service

Le service doit d'abord être enregistré en tant que service Windows. Pour ce faire, double-cliquez sur 1_install_service.bat. En cas de succès, une fenêtre d'invite de commande apparaîtra et disparaîtra immédiatement. Sinon, un message apparaîtra.

Démarrer le service

Vous pouvez démarrer le service de moteur de base de données H2 à l’aide du gestionnaire de services de Windows ou en double-cliquant sur 2_start_service.bat. Veuillez noter que le fichier de commandes n'imprime pas de message d'erreur si le service n'est pas installé.

Se connecter à la console H2

Après l'installation et le démarrage du service, vous pouvez vous connecter à l'application H2 Console à l'aide d'un navigateur. En double cliquant sur 3_start_browser.bat pour faire ça. Le port par défaut (8082) est codé en dur dans le fichier de commandes.

Arrêtez le service

Pour arrêter le service, double-cliquez sur 4_stop_service.bat. Veuillez noter que le fichier de commandes n'imprime pas de message d'erreur si le service n'est pas installé ou démarré.

Désinstaller le service

Pour désinstaller le service, double-cliquez sur 5_uninstall_service.bat. En cas de succès, une fenêtre d'invite de commande apparaîtra et disparaîtra immédiatement. Sinon, un message apparaîtra.

Pilotes JDBC supplémentaires

Pour utiliser d'autres bases de données (par exemple, MySQL), l'emplacement des pilotes JDBC de ces bases de données doit être ajouté aux variables d'environnement. H2DRIVERS ou CLASSPATH avant d'installer le service. Plusieurs pilotes peuvent être définis; chaque entrée doit être séparée par un ; (Windows) ou : (autres systèmes d'exploitation). Les espaces dans les noms de chemin sont pris en charge. Les paramètres ne doivent pas être cités.

Pilote ODBC

Cette base de données ne dispose pas actuellement de son propre pilote ODBC, mais elle prend en charge le protocole réseau PostgreSQL. Par conséquent, le pilote ODBC PostgreSQL peut être utilisé. La prise en charge du protocole réseau PostgreSQL est relativement nouvelle et doit être considérée comme expérimentale. Il ne doit pas être utilisé pour des applications de production.

Pour utiliser le pilote ODBC PostgreSQL sur les versions 64 bits de Windows, exécutez d’abord c: /windows/syswow64/odbcad32.exe. À ce stade, vous configurez votre DSN comme vous le feriez sur n'importe quel autre système. Voir aussi: Re: Pilote ODBC sous Windows 64 bits

Installation ODBC

Tout d'abord, le pilote ODBC doit être installé. Tout pilote récent PostgreSQL ODBC devrait fonctionner, cependant la version 8.2 (psqlodbc-08_02 *) ou plus récent est recommandé. La version Windows du pilote ODBC PostgreSQL est disponible à l'adresse https://www.postgresql.org/ftp/odbc/versions/msi/.

Démarrer le serveur

Après avoir installé le pilote ODBC, démarrez le serveur H2 à l’aide de la ligne de commande suivante:




java -cp h2 * .jar org.h2.tools.Server

Le serveur PG (protocole PG pour PostgreSQL) est également lancé. Par défaut, les bases de données sont stockées dans le répertoire de travail actuel où le serveur est démarré. Utilisation -baseDir pour enregistrer des bases de données dans un autre répertoire, par exemple le répertoire de base de l'utilisateur:




java -cp h2 * .jar org.h2.tools.Server -baseDir ~

Le serveur PG peut être démarré et arrêté depuis une application Java comme suit:




Server server = Server.createPgServer ("- baseDir", "~");
server.start ();
...
server.stop ();

Par défaut, seules les connexions de localhost sont autorisées. Pour autoriser les connexions distantes, utilisez -pgAllowOthers lors du démarrage du serveur.

Pour mapper un nom de base de données ODBC sur un nom de base de données JDBC différent, utilisez l'option -clé lors du démarrage du serveur. Veuillez noter qu'un seul mappage est autorisé. Ce qui suit mappera la base de données ODBC nommée TESTER à l'URL de la base de données jdbc: h2: ~ / data / test; cipher = aes:




java org.h2.tools.Server -pg -key TEST "~ / data / test; cipher = aes"

Configuration ODBC

Après l’installation du pilote, une nouvelle source de données doit être ajoutée. Sous Windows, lancez odbcad32.exe ouvrir l'administrateur de la source de données. Cliquez ensuite sur 'Ajouter …' et sélectionnez le pilote PostgreSQL Unicode. Cliquez ensuite sur 'Terminer'. Vous pourrez modifier les propriétés de la connexion. La colonne de propriété représente la clé de propriété dans le odbc.ini fichier (qui peut être différent de l'interface graphique).

Propriété Exemple Remarques
La source de données Test H2 Le nom de la source de données ODBC
Base de données ~ / test; ifexists = true Le nom de la base de données. Cela peut inclure les paramètres de connexion. Par défaut, la base de données est stockée dans le répertoire de travail actuel dans lequel le serveur est démarré, sauf lorsque le paramètre -baseDir est utilisé. Le nom doit comporter au moins 3 caractères.
Nom du serveur localhost Le nom du serveur ou l'adresse IP.
Par défaut, seules les connexions distantes sont autorisées.
Nom d'utilisateur sa Le nom d'utilisateur de la base de données.
SSL faux (désactivé) Pour l'instant, SSL n'est pas pris en charge.
Port 5435 Le port sur lequel le serveur PG écoute.
Mot de passe sa Le mot de passe de la base de données.

Pour améliorer les performances, veuillez activer «Préparation côté serveur» sous Options / Source de données / Page 2 / Préparation côté serveur.

Ensuite, vous pouvez utiliser cette source de données.

Limites de prise en charge du protocole PG

Actuellement, seul un sous-ensemble du protocole réseau PostgreSQL est implémenté. En outre, il peut y avoir des problèmes de compatibilité au niveau SQL, avec le catalogue ou avec le codage de texte. Les problèmes sont corrigés au fur et à mesure qu'ils sont trouvés. Actuellement, les instructions ne peuvent pas être annulées lors de l’utilisation du protocole PG. De plus, H2 ne fournit pas d'index méta sur ODBC.

Le pilote ODBC PostgreSQL Setup nécessite un mot de passe de base de données; cela signifie qu'il n'est pas possible de se connecter aux bases de données H2 sans mot de passe. Ceci est une limitation du pilote ODBC.

Considérations de sécurité

Actuellement, le serveur PG ne prend pas en charge les réponses aux demandes d’interrogation ni les mots de passe. Cela peut poser problème si un attaquant peut écouter les données transférées entre le pilote ODBC et le serveur, car le mot de passe est lisible par l’attaquant. De plus, il n’est actuellement pas possible d’utiliser des connexions SSL chiffrées. Par conséquent, le pilote ODBC ne doit pas être utilisé lorsque la sécurité est importante.

La première connexion qui ouvre une base de données à l'aide du serveur PostgreSQL doit être un utilisateur administrateur. Les connexions suivantes ne doivent pas nécessairement être ouvertes par un administrateur.

Utiliser Microsoft Access

Lorsque vous utilisez Microsoft Access pour modifier des données dans une table H2 liée, vous devrez peut-être activer l'option suivante: Outils – Options – Modifier / Rechercher – Champs ODBC.

Utiliser H2 dans Microsoft .NET

La base de données peut être utilisée à partir de Microsoft .NET même sans Java, en utilisant IKVM.NET. Vous pouvez accéder à une base de données H2 sur .NET à l'aide de l'API JDBC ou de l'interface ADO.NET.

Utilisation de l'API ADO.NET sur .NET

Une implémentation de l'interface ADO.NET est disponible dans le projet open source H2Sharp.

Utilisation de l'API JDBC sur .NET

  • Installez le .NET Framework de Microsoft. Mono n'a pas encore été testé.
  • Installez IKVM.NET.
  • Copier le h2 * .jar déposer dans ikvm / bin
  • Exécutez la console H2 en utilisant: ikvm -jar h2 * .jar
  • Convertir la H2 Console en un .EXE déposer en utilisant: ikvmc -target: winexe h2 * .jar. Vous pouvez ignorer les avertissements.
  • Créer un .dll fichier en utilisant (changer la version en conséquence): ikvmc.exe -target: bibliothèque -version: 1.0.69.0 h2 * .jar

Si vous souhaitez que votre application C # utilise H2, vous devez ajouter le h2.dll et le IKVM.OpenJDK.ClassLibrary.dll à votre solution C #. Voici quelques exemples de code:




en utilisant le système;
en utilisant java.sql;

test de classe

    static public void Main ()
    
        org.h2.Driver.load ();
        Connexion conn = DriverManager.getConnection ("jdbc: h2: ~ / test", "sa", "sa");
        Instruction stat = conn.createStatement ();
        ResultSet rs = stat.executeQuery ("SELECT 'Hello World" ");
        while (rs.next ())
        
            Console.WriteLine (rs.getString (1));
        
    

ACIDE

Dans le monde des bases de données, ACID signifie:

  • Atomicité: les transactions doivent être atomiques, c'est-à-dire que toutes les tâches sont effectuées ou aucune.
  • Cohérence: toutes les opérations doivent respecter les contraintes définies.
  • Isolement: les transactions doivent être isolées les unes des autres.
  • Durabilité: la transaction engagée ne sera pas perdue.

L'atomicité

Les transactions dans cette base de données sont toujours atomiques.

Cohérence

Par défaut, cette base de données est toujours dans un état cohérent. Les règles d'intégrité référentielle sont appliquées sauf lorsqu'elles sont explicitement désactivées.

Isolement

Pour H2, comme pour la plupart des autres systèmes de base de données, le niveau d'isolation par défaut est "Lecture validée". Cela fournit de meilleures performances, mais signifie également que les transactions ne sont pas complètement isolées. H2 prend en charge les niveaux d'isolation de transaction "sérialisable", "lecture validée" et "lecture non validée".

Durabilité

Cette base de données ne garantit pas que toutes les transactions validées survivent à une panne de courant. Les tests montrent que toutes les bases de données perdent parfois des transactions en cas de panne de courant (pour plus de détails, voir ci-dessous). Lorsque les transactions perdues ne sont pas acceptables, un ordinateur portable ou un UPS (alimentation sans coupure) devrait être utilisé. Si la durabilité est requise dans tous les cas possibles de défaillance matérielle, vous devez utiliser la mise en cluster, telle que le mode de mise en cluster H2.

Problèmes de durabilité

La durabilité complète signifie que toutes les transactions engagées survivent à une panne de courant. Certaines bases de données prétendent pouvoir garantir la durabilité, mais de telles affirmations sont fausses. Un test de durabilité a été exécuté sur H2, HSQLDB, PostgreSQL et Derby. Toutes ces bases de données perdent parfois des transactions validées. Le test est inclus dans le téléchargement H2, voir org.h2.test.poweroff.Test.

Façons d'atteindre (non) la durabilité

S'assurer que les transactions validées ne sont pas perdues est plus compliqué qu'il n'y paraît. Pour garantir la durabilité complète, une base de données doit s'assurer que l'enregistrement du journal se trouve sur le disque dur avant le retour de l'appel de validation. Pour ce faire, les bases de données utilisent différentes méthodes. L'une consiste à utiliser le mode d'accès au fichier «écriture synchrone». En Java, RandomAccessFile supporte les modes rws et rwd:

  • rwd: chaque mise à jour du contenu du fichier est écrite de manière synchrone sur le périphérique de stockage sous-jacent.
  • rws: en plus de rwd, chaque mise à jour des métadonnées est écrite de manière synchrone.

Un examen (org.h2.test.poweroff.TestWrite) avec l’un de ces modes réalise environ 50 000 opérations d’écriture par seconde. Même lorsque le tampon d'écriture du système d'exploitation est désactivé, le taux d'écriture est d'environ 50 000 opérations par seconde. Cette fonctionnalité ne force pas les modifications sur le disque car elle ne vide pas tous les tampons. Le test met à jour le même octet dans le fichier. Si le disque dur était capable d'écrire à cette vitesse, il devrait alors faire au moins 50 000 tours par seconde, ou 3 millions de tours par minute. Il n'y a pas de tels disques durs. Le disque dur utilisé pour le test est d’environ 7200 tr / min, soit environ 120 tours par seconde. Il y a une surcharge, donc le taux d'écriture maximal doit être inférieur à celui.

Appel fsync purge les tampons. Il y a deux façons de faire cela en Java:

  • FileDescriptor.sync (). La documentation indique que cela oblige tous les tampons système à se synchroniser avec le périphérique sous-jacent. Cette méthode est censée renvoyer une fois que toutes les copies de mémoire tampon associées à ce descripteur de fichier modifiées en mémoire ont été écrites sur le support physique.
  • FileChannel.force (). Cette méthode est supposée forcer toutes les mises à jour du fichier de ce canal à être écrites sur le périphérique de stockage qui le contient.

Par défaut, les appels MySQL fsync pour chaque commit. Lorsque vous utilisez l'une de ces méthodes, vous ne pouvez réaliser qu'environ 60 opérations d'écriture par seconde, ce qui correspond au taux de rotation par minute du disque dur utilisé. Malheureusement, même en appelant FileDescriptor.sync () ou FileChannel.force (), les données ne sont pas toujours conservées sur le disque dur, car la plupart des disques durs n'obéissent pas fsync (): voir votre disque dur est à vous. Sous Mac OS X, fsync ne vide pas les tampons de disque dur. Voir Bad fsync?. La situation est donc source de confusion et les tests prouvent qu'il y a un problème.

Il est difficile de vider les mémoires tampons des disques durs et, si vous le faites, les performances sont très mauvaises. Tout d’abord, vous devez vous assurer que le disque dur vide réellement tous les tampons. Les tests montrent que cela ne peut pas être fait de manière fiable. Ensuite, le nombre maximal de transactions est d'environ 60 par seconde. Pour ces raisons, le comportement par défaut de H2 consiste à retarder l'écriture des transactions validées.

En H2, après une panne de courant, un peu plus d'une seconde de transactions validées peut être perdue. Pour changer le comportement, utilisez SET WRITE_DELAY et CHECKPOINT SYNC. La plupart des autres bases de données prennent également en charge le délai de validation. Dans la comparaison des performances, le délai de validation a été utilisé pour toutes les bases de données qui le prennent en charge.

Exécution du test de durabilité

Pour tester la durabilité / non-durabilité de cette base de données et d'autres, vous pouvez utiliser l'application de test dans le package. org.h2.test.poweroff. Deux ordinateurs avec une connexion réseau sont requis pour exécuter ce test. Un ordinateur écoute simplement, pendant que l'application de test est exécutée (et le courant est coupé) sur l'autre ordinateur. L'ordinateur avec l'application d'écoute ouvre un port TCP / IP et écoute les connexions entrantes. Le deuxième ordinateur se connecte d'abord à l'écouteur, puis crée les bases de données et commence à insérer des enregistrements. La connexion est définie sur 'autocommit', ce qui signifie qu'après chaque enregistrement inséré, une validation est effectuée automatiquement. Ensuite, l'ordinateur de test notifie à l'écouteur que cet enregistrement a été inséré avec succès. The listener computer displays the last inserted record number every 10 seconds. Now, switch off the power manually, then restart the computer, and run the application again. You will find out that in most cases, none of the databases contains all the records that the listener computer knows about. For details, please consult the source code of the listener and test application.

le Recover tool can be used to extract the contents of a database file, even if the database is corrupted. It also extracts the content of the transaction log and large objects (CLOB or BLOB). To run the tool, type on the command line:




java -cp h2*.jar org.h2.tools.Recover

For each database in the current directory, a text file will be created. This file contains raw insert statements (for the data) and data definition (DDL) statements to recreate the schema of the database. This file can be executed using the RunScript tool or a RUNSCRIPT FROM SQL statement. The script includes at least one CREATE USER déclaration. If you run the script against a database that was created with the same user, or if there are conflicting users, running the script will fail. Consider running the script against a database that was created with a user name that is not in the script.

le Recover tool creates a SQL script from database file. It also processes the transaction log.

To verify the database can recover at any time, append ;RECOVER_TEST=64 to the database URL in your test environment. This will simulate an application crash after each 64 writes to the database file. A log file named databaseName.h2.db.log is created that lists the operations. The recovery is tested using an in-memory file system, that means it may require a larger heap setting.

File Locking Protocols

Multiple concurrent connections to the same database are supported, however a database file can only be open for reading and writing (in embedded mode) by one process at the same time. Otherwise, the processes would overwrite each others data and corrupt the database file. To protect against this problem, whenever a database is opened, a lock file is created to signal other processes that the database is in use. If the database is closed, or if the process that opened the database stops normally, this lock file is deleted.

In special cases (if the process did not terminate normally, for example because there was a power failure), the lock file is not deleted by the process that created it. That means the existence of the lock file is not a safe protocol for file locking. However, this software uses a challenge-response protocol to protect the database files. There are two methods (algorithms) implemented to provide both security (that is, the same database files cannot be opened by two processes at the same time) and simplicity (that is, the lock file does not need to be deleted manually by the user). The two methods are 'file method' and 'socket methods'.

The file locking protocols (except the file locking method 'FS') have the following limitation: if a shared file system is used, and the machine with the lock owner is sent to sleep (standby or hibernate), another machine may take over. If the machine that originally held the lock wakes up, the database may become corrupt. If this situation can occur, the application must ensure the database is closed when the application is put to sleep.

File Locking Method 'File'

The default method for database file locking for version 1.3 and older is the 'File Method'. The algorithm is:

  • If the lock file does not exist, it is created (using the atomic operation File.createNewFile). Then, the process waits a little bit (20 ms) and checks the file again. If the file was changed during this time, the operation is aborted. This protects against a race condition when one process deletes the lock file just after another one create it, and a third process creates the file again. It does not occur if there are only two writers.
  • If the file can be created, a random number is inserted together with the locking method ('file'). Afterwards, a watchdog thread is started that checks regularly (every second once by default) if the file was deleted or modified by another (challenger) thread / process. Whenever that occurs, the file is overwritten with the old data. The watchdog thread runs with high priority so that a change to the lock file does not get through undetected even if the system is very busy. However, the watchdog thread does use very little resources (CPU time), because it waits most of the time. Also, the watchdog only reads from the hard disk and does not write to it.
  • If the lock file exists and was recently modified, the process waits for some time (up to two seconds). If it was still changed, an exception is thrown (database is locked). This is done to eliminate race conditions with many concurrent writers. Afterwards, the file is overwritten with a new version (challenge). After that, the thread waits for 2 seconds. If there is a watchdog thread protecting the file, he will overwrite the change and this process will fail to lock the database. However, if there is no watchdog thread, the lock file will still be as written by this thread. In this case, the file is deleted and atomically created again. The watchdog thread is started in this case and the file is locked.

This algorithm is tested with over 100 concurrent threads. In some cases, when there are many concurrent threads trying to lock the database, they block each other (meaning the file cannot be locked by any of them) for some time. However, the file never gets locked by two threads at the same time. However using that many concurrent threads / processes is not the common use case. Generally, an application should throw an error to the user if it cannot open a database, and not try again in a (fast) loop.

File Locking Method 'Socket'

There is a second locking mechanism implemented, but disabled by default. To use it, append ;FILE_LOCK=SOCKET to the database URL. The algorithm is:

  • If the lock file does not exist, it is created. Then a server socket is opened on a defined port, and kept open. The port and IP address of the process that opened the database is written into the lock file.
  • If the lock file exists, and the lock method is 'file', then the software switches to the 'file' method.
  • If the lock file exists, and the lock method is 'socket', then the process checks if the port is in use. If the original process is still running, the port is in use and this process throws an exception (database is in use). If the original process died (for example due to a power failure, or abnormal termination of the virtual machine), then the port was released. The new process deletes the lock file and starts again.

This method does not require a watchdog thread actively polling (reading) the same file every second. The problem with this method is, if the file is stored on a network share, two processes (running on different computers) could still open the same database files, if they do not have a direct TCP/IP connection.

File Locking Method 'FS'

This is the default mode for version 1.4 and newer. This database file locking mechanism uses native file system lock on the database file. No *.lock.db file is created in this case, and no background thread is started. This mechanism may not work on all systems as expected. Some systems allow to lock the same file multiple times within the same virtual machine, and on some system native file locking is not supported or files are not unlocked after a power failure.

To enable this feature, append ;FILE_LOCK=FS to the database URL.

This feature is relatively new. When using it for production, please ensure your system does in fact lock files as expected.

Using Passwords

Using Secure Passwords

Remember that weak passwords can be broken regardless of the encryption and security protocols. Don't use passwords that can be found in a dictionary. Appending numbers does not make passwords secure. A way to create good passwords that can be remembered is: take the first letters of a sentence, use upper and lower case characters, and creatively include special characters (but it's more important to use a long password than to use special characters). Exemple:

i'sE2rtPiUKtT from the sentence it's easy to remember this password if you know the trick.

Passwords: Using Char Arrays instead of Strings

Java strings are immutable objects and cannot be safely 'destroyed' by the application. After creating a string, it will remain in the main memory of the computer at least until it is garbage collected. The garbage collection cannot be controlled by the application, and even if it is garbage collected the data may still remain in memory. It might also be possible that the part of memory containing the password is swapped to disk (if not enough main memory is available), which is a problem if the attacker has access to the swap file of the operating system.

It is a good idea to use char arrays instead of strings for passwords. Char arrays can be cleared (filled with zeros) after use, and therefore the password will not be stored in the swap file.

This database supports using char arrays instead of string to pass user and file passwords. The following code can be used to do that:




import java.sql.*;
import java.util.*;
public class Test 
    public static void main (String[] args) throws Exception 
        String url = "jdbc:h2:~/test";
        Properties prop = new Properties();
        prop.setProperty("user", "sa");
        System.out.print("Password?");
        carboniser[] password = System.console().readPassword();
        prop.put("password", password);
        Connection conn = null;
        essayer 
            conn = DriverManager.getConnection(url, prop);
         enfin 
            Arrays.fill(password, (char) 0);
        
        conn.close();
    

When using Swing, use javax.swing.JPasswordField.

Passing the User Name and/or Password in the URL

Instead of passing the user name as a separate parameter as in Connection conn = DriverManager. getConnection("jdbc:h2:~/test", "sa", "123"); the user name (and/or password) can be supplied in the URL itself: Connection conn = DriverManager. getConnection("jdbc:h2:~/test;USER=sa;PASSWORD=123"); The settings in the URL override the settings passed as a separate parameter.

Password Hash

Sometimes the database password needs to be stored in a configuration file (for example in the web.xml file). In addition to connecting with the plain text password, this database supports connecting with the password hash. This means that only the hash of the password (and not the plain text password) needs to be stored in the configuration file. This will only protect others from reading or re-constructing the plain text password (even if they have access to the configuration file); it does not protect others from accessing the database using the password hash.

To connect using the password hash instead of plain text password, append ;PASSWORD_HASH=TRUE to the database URL, and replace the password with the password hash. To calculate the password hash from a plain text password, run the following command within the H2 Console tool: @password_hash . As an example, if the user name is sa and the password is tester, run the command @password_hash SA test. Then use the resulting password hash as you would use the plain text password. When using an encrypted database, then the user password and file password need to be hashed separately. To calculate the hash of the file password, run: @password_hash file .

Protection against SQL Injection

What is SQL Injection

This database engine provides a solution for the security vulnerability known as 'SQL Injection'. Here is a short description of what SQL injection means. Some applications build SQL statements with embedded user input such as:




String sql = "SELECT * FROM USERS WHERE PASSWORD='"+pwd+"'";
ResultSet rs = conn.createStatement().executeQuery(sql);

If this mechanism is used anywhere in the application, and user input is not correctly filtered or encoded, it is possible for a user to inject SQL functionality or statements by using specially built input such as (in this example) this password: ' OR ''='. In this case the statement becomes:




SELECT * FROM USERS WHERE PASSWORD='' OR ''='';

Which is always true no matter what the password stored in the database is. For more information about SQL Injection, see Glossary and Links.

Disabling Literals

SQL Injection is not possible if user input is not directly embedded in SQL statements. A simple solution for the problem above is to use a prepared statement:




String sql = "SELECT * FROM USERS WHERE PASSWORD=?";
PreparedStatement prep = conn.prepareStatement(sql);
prep.setString(1, pwd);
ResultSet rs = prep.executeQuery();

This database provides a way to enforce usage of parameters when passing user input to the database. This is done by disabling embedded literals in SQL statements. To do this, execute the statement:




SET ALLOW_LITERALS NONE;

Afterwards, SQL statements with text and number literals are not allowed any more. That means, SQL statement of the form WHERE NAME='abc' ou WHERE CustomerId=10 will fail. It is still possible to use prepared statements and parameters as described above. Also, it is still possible to generate SQL statements dynamically, and use the Statement API, as long as the SQL statements do not include literals. There is also a second mode where number literals are allowed: SET ALLOW_LITERALS NUMBERS. To allow all literals, execute SET ALLOW_LITERALS ALL (this is the default setting). Literals can only be enabled or disabled by an administrator.

Using Constants

Disabling literals also means disabling hard-coded 'constant' literals. This database supports defining constants using the CREATE CONSTANT commander. Constants can be defined only when literals are enabled, but used even when literals are disabled. To avoid name clashes with column names, constants can be defined in other schemas:




CREATE SCHEMA CONST AUTHORIZATION SA;
CREATE CONSTANT CONST.ACTIVE VALUE 'Active';
CREATE CONSTANT CONST.INACTIVE VALUE 'Inactive';
SELECT * FROM USERS WHERE TYPE=CONST.ACTIVE;

Even when literals are enabled, it is better to use constants instead of hard-coded number or text literals in queries or views. With constants, typos are found at compile time, the source code is easier to understand and change.

Using the ZERO() Function

It is not required to create a constant for the number 0 as there is already a built-in function ZERO():




SELECT * FROM USERS WHERE LENGTH(PASSWORD)=ZERO();

Protection against Remote Access

By default this database does not allow connections from other machines when starting the H2 Console, the TCP server, or the PG server. Remote access can be enabled using the command line options -webAllowOthers, -tcpAllowOthers, -pgAllowOthers.

If you enable remote access using -tcpAllowOthers ou -pgAllowOthers, please also consider using the options -baseDir, so that remote users can not create new databases or access existing databases with weak passwords. When using the option -baseDir, only databases within that directory may be accessed. Ensure the existing accessible databases are protected using strong passwords.

If you enable remote access using -webAllowOthers, please ensure the web server can only be accessed from trusted networks. The options -baseDir don't protect access to the tools section, prevent remote shutdown of the web server, changes to the preferences, the saved connection settings, or access to other databases accessible from the system.

Restricting Class Loading and Usage

By default there is no restriction on loading classes and executing Java code for admins. That means an admin may call system functions such as System.setProperty by executing:




CREATE ALIAS SET_PROPERTY FOR "java.lang.System.setProperty";
CALL SET_PROPERTY('abc', '1');
CREATE ALIAS GET_PROPERTY FOR "java.lang.System.getProperty";
CALL GET_PROPERTY('abc');

To restrict users (including admins) from loading classes and executing code, the list of allowed classes can be set in the system property h2.allowedClasses in the form of a comma separated list of classes or patterns (items ending with *). By default all classes are allowed. Exemple:




java -Dh2.allowedClasses=java.lang.Math,com.acme.*

This mechanism is used for all user classes, including database event listeners, trigger classes, user-defined functions, user-defined aggregate functions, and JDBC driver classes (with the exception of the H2 driver) when using the H2 Console.

Security Protocols

The following paragraphs document the security protocols used in this database. These descriptions are very technical and only intended for security experts that already know the underlying security primitives.

User Password Encryption

When a user tries to connect to a database, the combination of user name, @, and password are hashed using SHA-256, and this hash value is transmitted to the database. This step does not protect against an attacker that re-uses the value if he is able to listen to the (unencrypted) transmission between the client and the server. But, the passwords are never transmitted as plain text, even when using an unencrypted connection between client and server. That means if a user reuses the same password for different things, this password is still protected up to some point. See also 'RFC 2617 – HTTP Authentication: Basic and Digest Access Authentication' for more information.

When a new database or user is created, a new random salt value is generated. The size of the salt is 64 bits. Using the random salt reduces the risk of an attacker pre-calculating hash values for many different (commonly used) passwords.

The combination of user-password hash value (see above) and salt is hashed using SHA-256. The resulting value is stored in the database. When a user tries to connect to the database, the database combines user-password hash value with the stored salt value and calculates the hash value. Other products use multiple iterations (hash the hash value again and again), but this is not done in this product to reduce the risk of denial of service attacks (where the attacker tries to connect with bogus passwords, and the server spends a lot of time calculating the hash value for each password). The reasoning is: if the attacker has access to the hashed passwords, he also has access to the data in plain text, and therefore does not need the password any more. If the data is protected by storing it on another computer and only accessible remotely, then the iteration count is not required at all.

File Encryption

The database files can be encrypted using the AES-128 algorithm.

When a user tries to connect to an encrypted database, the combination of file@ and the file password is hashed using SHA-256. This hash value is transmitted to the server.

When a new database file is created, a new cryptographically secure random salt value is generated. The size of the salt is 64 bits. The combination of the file password hash and the salt value is hashed 1024 times using SHA-256. The reason for the iteration is to make it harder for an attacker to calculate hash values for common passwords.

The resulting hash value is used as the key for the block cipher algorithm. Then, an initialization vector (IV) key is calculated by hashing the key again using SHA-256. This is to make sure the IV is unknown to the attacker. The reason for using a secret IV is to protect against watermark attacks.

Before saving a block of data (each block is 8 bytes long), the following operations are executed: first, the IV is calculated by encrypting the block number with the IV key (using the same block cipher algorithm). This IV is combined with the plain text using XOR. The resulting data is encrypted using the AES-128 algorithm.

When decrypting, the operation is done in reverse. First, the block is decrypted using the key, and then the IV is calculated combined with the decrypted text using XOR.

Therefore, the block cipher mode of operation is CBC (cipher-block chaining), but each chain is only one block long. The advantage over the ECB (electronic codebook) mode is that patterns in the data are not revealed, and the advantage over multi block CBC is that flipped cipher text bits are not propagated to flipped plaintext bits in the next block.

Database encryption is meant for securing the database while it is not in use (stolen laptop and so on). It is not meant for cases where the attacker has access to files while the database is in use. When he has write access, he can for example replace pieces of files with pieces of older versions and manipulate data like this.

File encryption slows down the performance of the database engine. Compared to unencrypted mode, database operations take about 2.5 times longer using AES (embedded mode).

Wrong Password / User Name Delay

To protect against remote brute force password attacks, the delay after each unsuccessful login gets double as long. Use the system properties h2.delayWrongPasswordMin et h2.delayWrongPasswordMax to change the minimum (the default is 250 milliseconds) or maximum delay (the default is 4000 milliseconds, or 4 seconds). The delay only applies for those using the wrong password. Normally there is no delay for a user that knows the correct password, with one exception: after using the wrong password, there is a delay of up to (randomly distributed) the same delay as for a wrong password. This is to protect against parallel brute force attacks, so that an attacker needs to wait for the whole delay. Delays are synchronized. This is also required to protect against parallel attacks.

There is only one exception message for both wrong user and for wrong password, to make it harder to get the list of user names. It is not possible from the stack trace to see if the user name was wrong or the password.

HTTPS Connections

The web server supports HTTP and HTTPS connections using SSLServerSocket. There is a default self-certified certificate to support an easy starting point, but custom certificates are supported as well.

TLS Connections

Remote TLS connections are supported using the Java Secure Socket Extension (SSLServerSocket, SSLSocket). By default, anonymous TLS is enabled.

To use your own keystore, set the system properties javax.net.ssl.keyStore et javax.net.ssl.keyStorePassword before starting the H2 server and client. See also Customizing the Default Key and Trust Stores, Store Types, and Store Passwords for more information.

To disable anonymous TLS, set the system property h2.enableAnonymousTLS to false.

Universally Unique Identifiers (UUID)

This database supports UUIDs. Also supported is a function to create new UUIDs using a cryptographically strong pseudo random number generator. With random UUIDs, the chance of two having the same value can be calculated using the probability theory. See also 'Birthday Paradox'. Standardized randomly generated UUIDs have 122 random bits. 4 bits are used for the version (Randomly generated UUID), and 2 bits for the variant (Leach-Salz). This database supports generating such UUIDs using the built-in function RANDOM_UUID() ou UUID(). Here is a small program to estimate the probability of having two identical UUIDs after generating a number of values:




public class Test 
    public static void main (String[] args) throws Exception 
        double x = Math.pow(2, 122);
        for (int i = 35; i < 62; i++) 
            double n = Math.pow(2, i);
            double p = 1 - Math.exp(-(n * n) / 2 / x);
            System.out.println("2^" + i + "=" + (1L << i) +
                    " probability: 0" +
                    String.valueOf(1 + p).substring(1));
        
    

Some values are:

Number of UUIs Probability of Duplicates
2^36=68'719'476'736 0.000'000'000'000'000'4
2^41=2'199'023'255'552 0.000'000'000'000'4
2^46=70'368'744'177'664 0.000'000'000'4

To help non-mathematicians understand what those numbers mean, here a comparison: one's annual risk of being hit by a meteorite is estimated to be one chance in 17 billion, that means the probability is about 0.000'000'000'06.

Spatial Features

H2 supports the geometry data type and spatial indexes. Here is an example SQL script to create a table with a spatial column and index:




CREATE TABLE GEO_TABLE(GID SERIAL, THE_GEOM GEOMETRY);
INSERT INTO GEO_TABLE(THE_GEOM) VALUES
    ('POINT(500 505)'),
    ('LINESTRING(550 551, 525 512, 565 566)'),
    ('POLYGON ((550 521, 580 540, 570 564, 512 566, 550 521))');
CREATE SPATIAL INDEX GEO_TABLE_SPATIAL_INDEX
    ON GEO_TABLE(THE_GEOM);

To query the table using geometry envelope intersection, use the operation &&, as in PostGIS:




SELECT * FROM GEO_TABLE
    WHERE THE_GEOM &&
    'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))';

You can verify that the spatial index is used using the "explain plan" feature:




EXPLAIN SELECT * FROM GEO_TABLE
    WHERE THE_GEOM &&
    'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))';
-- Result
SÉLECTIONNER
    "GEO_TABLE"."GID",
    "GEO_TABLE"."THE_GEOM"
FROM "PUBLIC"."GEO_TABLE"
    /* PUBLIC.GEO_TABLE_SPATIAL_INDEX:
    THE_GEOM &&
    'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))'::Geometry */
WHERE INTERSECTS("THE_GEOM",
    'POLYGON ((490 490, 536 490, 536 515, 490 515, 490 490))'::Geometry)

For persistent databases, the spatial index is stored on disk; for in-memory databases, the index is kept in memory.

Recursive Queries

H2 has experimental support for recursive queries using so called "common table expressions" (CTE). Exemples:




WITH RECURSIVE T(N) AS (
    SELECT 1
    UNION ALL
    SELECT N+1 FROM T WHERE N<10
)
SELECT * FROM T;
-- returns the values 1 .. 10

WITH RECURSIVE T(N) AS (
    SELECT 1
    UNION ALL
    SELECT N*2 FROM T WHERE N<10
)
SELECT * FROM T;
-- returns the values 1, 2, 4, 8, 16

CREATE TABLE FOLDER(ID INT PRIMARY KEY, NAME VARCHAR(255), PARENT INT);

INSERT INTO FOLDER VALUES(1, null, null), (2, 'src', 1),
(3, 'main', 2), (4, 'org', 3), (5, 'test', 2);

WITH LINK(ID, NAME, LEVEL) AS (
    SELECT ID, NAME, 0 FROM FOLDER WHERE PARENT IS NULL
    UNION ALL
    SELECT FOLDER.ID, IFNULL(LINK.NAME || '/', '') || FOLDER.NAME, LEVEL + 1
    FROM LINK INNER JOIN FOLDER ON LINK.ID = FOLDER.PARENT
)
SELECT NAME FROM LINK WHERE NAME IS NOT NULL ORDER BY ID;
-- src
-- src/main
-- src/main/org
-- src/test

Limitations: Recursive queries need to be of the type UNION ALL, and the recursion needs to be on the second part of the query. No tables or views with the name of the table expression may exist. Different table expression names need to be used when using multiple distinct table expressions within the same transaction and for the same session. All columns of the table expression are of type VARCHAR, and may need to be cast to the required data type. Views with recursive queries are not supported. Subqueries and INSERT INTO ... FROM with recursive queries are not supported. Parameters are only supported within the last SÉLECTIONNER statement (a workaround is to use session variables like @start within the table expression). La syntaxe est la suivante:




WITH RECURSIVE recursiveQueryName(columnName, ...) AS (
    nonRecursiveSelect
    UNION ALL
    recursiveSelect
)
sélectionner

Settings Read from System Properties

Some settings of the database can be set on the command line using -DpropertyName=value. It is usually not required to change those settings manually. The settings are case sensitive. Exemple:




java -Dh2.serverCachedObjects=256 org.h2.tools.Server

The current value of the settings can be read in the table INFORMATION_SCHEMA.SETTINGS.

For a complete list of settings, see SysProperties.

Setting the Server Bind Address

Usually server sockets accept connections on any/all local addresses. This may be a problem on multi-homed hosts. To bind only to one address, use the system property h2.bindAddress. This setting is used for both regular server sockets and for TLS server sockets. IPv4 and IPv6 address formats are supported.

Pluggable File System

This database supports a pluggable file system API. The file system implementation is selected using a file name prefix. Internally, the interfaces are very similar to the Java 7 NIO2 API, but do not (yet) use or require Java 7. The following file systems are included:

  • zip: read-only zip-file based file system. Format: zip:/zipFileName!/fileName.
  • split: file system that splits files in 1 GB files (stackable with other file systems).
  • nio: file system that uses FileChannel au lieu de RandomAccessFile (faster in some operating systems).
  • nioMapped: file system that uses memory mapped files (faster in some operating systems). Please note that there currently is a file size limitation of 2 GB when using this file system. To work around this limitation, combine it with the split file system: split:nioMapped:test.
  • async: experimental file system that uses AsynchronousFileChannel au lieu de RandomAccessFile (faster in some operating systems).
  • memFS: in-memory file system (slower than mem; experimental; mainly used for testing the database engine itself).
  • memLZF: compressing in-memory file system (slower than memFS but uses less memory; experimental; mainly used for testing the database engine itself).
  • nioMemFS: stores data outside of the VM's heap – useful for large memory DBs without incurring GC costs.
  • nioMemLZF: stores compressed data outside of the VM's heap – useful for large memory DBs without incurring GC costs. Use "nioMemLZF:12:" to tweak the % of blocks that are stored uncompressed. If you size this to your working set correctly, compressed storage is roughly the same performance as uncompressed. The default value is 1%.

As an example, to use the nio file system with PageStore storage engine, use the following database URL: jdbc:h2:nio:~/test;MV_STORE=FALSE. With MVStore storage engine nio file system is used by default.

To register a new file system, extend the classes org.h2.store.fs.FilePath, FileBase, and call the method FilePath.register before using it.

For input streams (but not for random access files), URLs may be used in addition to the registered file systems. Exemple: jar:file:///c:/temp/example.zip!/org/example/nested.csv. To read a stream from the classpath, use the prefix classpath:, as in classpath:/org/h2/samples/newsfeed.sql.

Split File System

The file system prefix split: is used to split logical files into multiple physical files, for example so that a database can get larger than the maximum file system size of the operating system. If the logical file is larger than the maximum file size, then the file is split as follows:

  • (first block, is always created)
  • .1.part (second block)

More physical files (*.2.part, *.3.part) are automatically created / deleted if needed. The maximum physical file size of a block is 2^30 bytes, which is also called 1 GiB or 1 GB. However this can be changed if required, by specifying the block size in the file name. The file name format is: split:: where the file size per block is 2^x. For 1 MiB block sizes, use x = 20 (because 2^20 is 1 MiB). The following file name means the logical file is split into 1 MiB blocks: split:20:test.h2.db. An example database URL for this case is jdbc:h2:split:20:~/test.

Database Upgrade

In version 1.2, H2 introduced a new file store implementation which is incompatible to the one used in versions < 1.2. To automatically convert databases to the new file store, it is necessary to include an additional jar file. The file can be found at http://h2database.com/h2mig_pagestore_addon.jar . If this file is in the classpath, every connect to an older database will result in a conversion process.

The conversion itself is done internally via 'script to' et 'runscript from'. After the conversion process, the files will be renamed from

  • dbName.data.db à dbName.data.db.backup
  • dbName.index.db à dbName.index.db.backup

by default. Also, the temporary script will be written to the database directory instead of a temporary directory. Both defaults can be customized via

  • org.h2.upgrade.DbUpgrade.setDeleteOldDb(boolean)
  • org.h2.upgrade.DbUpgrade.setScriptInTmpDir(boolean)

prior opening a database connection.

Since version 1.2.140 it is possible to let the old h2 classes (v 1.2.128) connect to the database. The automatic upgrade .jar file must be present, and the URL must start with jdbc:h2v1_1: (the JDBC driver class is org.h2.upgrade.v1_1.Driver). If the database should automatically connect using the old version if a database with the old format exists (without upgrade), and use the new version otherwise, then append ;NO_UPGRADE=TRUE to the database URL. Please note the old driver did not process the system property "h2.baseDir" correctly, so that using this setting is not supported when upgrading.

Java Objects Serialization

Java objects serialization is enabled by default for columns of type OTHER, using standard Java serialization/deserialization semantics.

To disable this feature set the system property h2.serializeJavaObject=false (default: true).

Serialization and deserialization of java objects is customizable both at system level and at database level providing a JavaObjectSerializer implementation:

Custom Data Types Handler API

It is possible to extend the type system of the database by providing your own implementation of minimal required API basically consisting of type identification and conversion routines.

In order to enable this feature, set the system property h2.customDataTypesHandler (default: null) to the fully qualified name of the class providing CustomDataTypesHandler interface implementation.
The instance of that class will be created by H2 and used to:

  • resolve the names and identifiers of extrinsic data types.
  • convert values of extrinsic data types to and from values of built-in types.
  • provide order of the data types.

This is a system-level setting, i.e. affects all the databases.

Remarque: Please keep in mind that this feature may not possibly provide the same ABI stability level as other features as it exposes many of the H2 internals. You may be required to update your code occasionally due to internal changes in H2 if you are going to use this feature.

Limits and Limitations

This database has the following known limitations:

Term La description
AES-128 A block encryption algorithm. See also: Wikipedia: Advanced Encryption Standard
Birthday Paradox Describes the higher than expected probability that two persons in a room have the same birthday. Also valid for randomly generated UUIDs. See also: Wikipedia: Birthday problem
Digest Protocol to protect a password (but not to protect data). See also: RFC 2617: HTTP Digest Access Authentication
HTTPS A protocol to provide security to HTTP connections. See also: RFC 2818: HTTP Over TLS
Modes of Operation Wikipedia: Block cipher mode of operation
Sel Random number to increase the security of passwords. See also: Wikipedia: Key derivation function
SHA-256 A cryptographic one-way hash function. See also: Wikipedia: Secure Hash Algorithms
SQL Injection A security vulnerability where an application embeds SQL statements or expressions in user input. See also: Wikipedia: SQL injection
Watermark Attack Security problem of certain encryption programs where the existence of certain data can be proven without decrypting. For more information, search in the internet for 'watermark attack cryptoloop'
SSL/TLS Secure Sockets Layer / Transport Layer Security. See also: Java Secure Socket Extension (JSSE)

Click to rate this post!
[Total: 0 Average: 0]

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.