Le problème :
Après mon article "C'est galère d'installer Oracle XE sur windows 7 64 bits. Parfois ça marche, parfois ça marche pas." Je me suis décidé à utiliser une autre distribution non supportée pour installer Oracle.
Si, si...
Bon après CentOS c'est bien aussi (y'a pas fail2ban par défaut mais c'est bien quand même.).
Pour des bases de développement évidemment. Je ne veux pas voir ça ailleurs..
Solution :
On utilise Oracle EE disponible sur le site d'Oracle
- Creation user oracle
- Mot de passe oracle
- Mettre à jour le .bashrc comme on veut
- xhost + en root pour utiliser l'installateur graphique avec l'utilisateur oracle
- Les binaires se téléchargent ici : https://wiki.debian.org/DataBase/Oracle
- Attention, pas la distribution zlinux évidemment
- Les prérequis des librairies (et un certains nombres de tâches décrites ci-dessus) :
- https://github.com/antonioluna/oracle12c-debian/blob/master/Pre-install.sh
- c'est trop bien...
- Dans le .bashrc :
export ORACLE_HOME
export ORACLE_SID
- Installation de rlwrap si lors du lancement de sqlplus ca merde
apt-get install rlwrap
- Dans mon user oracle
cd ~/oracle-product/product/12.1.0/dbhome_1/dbs$
mv init.ora /home/oracle/oracle-product/product/12.1.0/dbhome_1/dbs/init<MABASE>.or
- Creation du repertoire de la base
mkdir /home/oracle/oradata/<MA BASE>
- create database
- https://docs.oracle.com/cd/B28359_01/server.111/b28310/create003.htm#ADMIN1107
- Editer le fichier /home/oracle/oracle-product/product/12.1.0/dbhome_1/dbs/init<MABASE>.ora
# Change '<ORACLE_BASE>' to point to the oracle base (the one you specify at
# install time)
db_name='MABASE'
memory_target=512M
processes = 150
audit_file_dest='/home/oracle/oracle-product/admin/orcl/adump'
audit_trail ='db'
db_block_size=8192
db_domain=''
db_recovery_file_dest='/home/oracle/oracle-product/fast_recovery_area'
db_recovery_file_dest_size=2G
diagnostic_dest='/home/oracle/oracle-product'
dispatchers='(PROTOCOL=TCP) (SERVICE=ORCLXDB)'
open_cursors=300
remote_login_passwordfile='EXCLUSIVE'
undo_tablespace='UNDOTBS1'
# You may want to ensure that control files are created on separate physical
# devices
control_files = (/home/oracle/oradata/mabase/ora_control1, /home/oracle/oradata/mabase/ora_control2)
compatible ='11.2.0'
- Recommencer si cela n'a pas fonctionné :
rm -rf /home/oracle/oradata/mabase/*
- Creation du user : sqlplus / as sysdba
STARTUP NOMOUNT;
CREATE DATABASE mybase
USER SYS IDENTIFIED BY lozenge
USER SYSTEM IDENTIFIED BY lozenge
LOGFILE GROUP 1 ('/home/oracle/oradata/mybase/redo01.log') SIZE 100M,
GROUP 2 ('/home/oracle/oradata/mybase/redo02.log') SIZE 100M,
GROUP 3 ('/home/oracle/oradata/mybase/redo03.log') SIZE 100M
MAXLOGFILES 5
MAXLOGMEMBERS 5
MAXLOGHISTORY 1
MAXDATAFILES 100
CHARACTER SET WE8MSWIN1252
NATIONAL CHARACTER SET AL16UTF16
EXTENT MANAGEMENT LOCAL
DATAFILE '/home/oracle/oradata/mybase/system01.dbf' SIZE 200M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED
SYSAUX DATAFILE '/home/oracle/oradata/mybase/sysaux01.dbf' SIZE 200M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED
DEFAULT TABLESPACE calv14
DATAFILE '/home/oracle/oradata/mybase/database.dbf'
SIZE 500M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED
DEFAULT TEMPORARY TABLESPACE tempts1
TEMPFILE '/home/oracle/oradata/mybase/temp01.dbf'
SIZE 350M REUSE
UNDO TABLESPACE undotbs1
DATAFILE '/home/oracle/oradata/mybase/undotbs01.dbf'
SIZE 350M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;
@${ORACLE_HOME}/rdbms/admin/catalog (long)
@${ORACLE_HOME}/rdbms/admin/catproc <-- dba_data_files est créé ici (encore plus long)
- Recommencer si cela n'a pas fonctionné :
- drop user userBase cascade;
- Creation du user :
define SG_SCHEMA = 'userBase'
define DEFAULT_TBS = 'mytbs'
define TEMP_TBS = 'tempts1'
CREATE USER &SG_SCHEMA IDENTIFIED BY password
DEFAULT TABLESPACE &DEFAULT_TBS
TEMPORARY TABLESPACE &TEMP_TBS
;
grant connect to &SG_SCHEMA;
grant resource to &SG_SCHEMA;
GRANT UNLIMITED TABLESPACE TO &SG_SCHEMA;
GRANT CREATE SYNONYM TO &SG_SCHEMA ;
GRANT CREATE VIEW TO &SG_SCHEMA ;
Le problème :
Un problème tout simple ou presque :)
Deux threads qui discutent avec la base de données, synchronisés par un "SELECT ... FOR UPDATE" sur le même objet.
Le deuxième thread n'a pas toujours le résultat du travail du premier thread !
Solution :
Alors j'écarte toutes les hypothèses qui m'ont été faites :
-
une seule transaction par thread de la prise de lock au commit
-
transaction en REQUIRED_NEW
-
le lock via le SELECT FOR UPDATE est la première instruction des threads
-
pas d'instruction DDL pendant les threads
-
le SELECT FOR UPDATE fonctionne correctement
La réponse : Mysql utilise par défaut le mode d'isolation READ REPEATABLE alors que la plupart des autres bases utilisent READ COMMITED.
Comme j'imagine que ce n'est pas évident, quelques explications :
Thread n°1 |
Thread n°2 |
Mysql Mode : REPEATABLE READ |
Mysql Mode : READ COMMITED |
SELECT tableLock FOR UPDATE WHERE id = 1 |
|
|
|
UPDATE user.status = 4 |
|
|
|
start thread n°2 |
|
|
|
|
SELECT tableLock FOR UPDATE WHERE id = 1 |
Snapshot de la base pour avoir des données constantes pendant la transaction |
Pas de snapshot, ce sont les dernières données en base |
Commit |
|
|
|
|
Obtention du lock APRES le commit |
|
|
|
select user.status |
status est null / non mis à jour |
status = 4 ! |
Remarques :
-
Le thread n°2 déclenché par le thread n°1 se met en attente du lock. En faisant un SELECT !. C'est là le problème. Le mode REPEATABLE READ fait un snapshot valable sur la base entière au premier select.
-
ce point (snapshot sur la base entière) n'est pas très explicite dans les documentations
Solutions :
-
passer MYSQL en mode READ COMMITED (un peu performant en plus...)
-
mettre les mises à jour des données autres dans des sous-transactions
-
envisager un autre mode de synchronisation hors transaction (cache distribué...)
Le problème :
Pas vraiment de problème si ce n'est le problème des drivers en 32 ou 64 bits. C'est plus un pense-bête.
EA <-> base Mysql via driver ODBC
La documentation d'installation du driver ODBC : ici
Solution :
-
Installer EA
-
Installer un kit Microsoft Visual C++ 2010. Attention à prendre la même version (32 ou 64 bits) que le driver ODBC
-
Installer un driver Mysql
-
Chez moi j'ai eu un problème :
-
si pas de driver x64, pas reconnu dans ODBC,
-
si pas de driver x32 EA ne se connecte pas (il est en 32 bits).
-
Bref il faut les deux... quelque soit le kit VC++....
-
Créer un utilisateur sur votre base MYSQL, une base, donner les droits à l'utilisateur pour accéder à la base
-
Créer les tables via les scripts EA
-
Créer une source de données ODBC
-
Panneau de configuration > Source de données (ODBC)
-
Source de données utilisateurs > Ajouter
-
Adresse du serveur + login/mdp + base
-
Ouvrir EA
-
Ouvrir le projet à commiter
-
-
Transfert :
-
-
-
Choisir le fichier source
-
Choisir la connexion ODBC et remplir les informations (utiliser la connection ODBC créée précdemment)
-
Cocher :
-
Ouvrir le projet avec EA
-
File | Open | Connect to server | Connection Wizard
-
Choisir la connexion ODBC et remplir les informations
Le problème :
Envers est un module core d'Hibernate qui permet d'auditer des entités, i.e, historiser toutes les modifications de l'objet.
Le principe est de stocker dans une table quasiment identique les différentes versions de l'objet.
Exemple :
Table source : UTILISATEUR(id, version, nom, adresse)
Table audit : UTILISATEUR_AUD(id, rev, nom, adresse)
Le champ version de la table source est utilisé pour l'optimist locking, on n'a donc pas besoin de ce champ dans la tableau auditée.
Par contre la table auditée a un champ un peu équivalent : "révision" qui permet de stocker les différentes versions de l'objet.
Attention cette révision est différente de la version de l'objet, en effet cela correspond plus à un numéro de commit global : tous les objets modifiés et audités dans une même transaction auront la même révision (un peu comme un commit SVN).
Coté code source :
@Audited
@Entity
public class UTILISATEUR {
@Id
private Long id;
@Version
private Long version;
private String nom;
private String adresse;
}
Facile comme tout (il manque des trucs bien sur : création de la table d'audit, des séquences qui vont bien, des informations supplémentaires trackées avec l'audit, etc...)
A chaque sauvegarde d'une instance UTILISATEUR, on aura une ligne dans UTILISATEUR_AUD.
Bon, mais que se passe-t-il si au lieu d'avoir 1 adresse, UTILISATEUR a N adresses ?
@Audited
@Entity
public class UTILISATEUR {
@Id
private Long id;
@Version
private Long version;
private String nom;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "UTILISATEUR_ID")
private List<String> adresses;
}
Solution :
Déjà lire la documentation.
Ensuite choisir : est-ce que les adresses doivent être auditée ? Si oui, on continue.
Evidemment il va y a voir une deuxième table ADRESSE_AUD.
Mais un des points forts d'envers est d'auditer seulement les deltas : une ADRESSE modifiée ne veut pas dire que UTILISATEUR est aussi modifié.
La conséquence importante est que là où on a une relation 1..N, on passe à une relation N..N. Pourquoi ?
Voici ce qui se passe :
// Sauvegarde d'un utilisateur avec une adresse, dans les tables d'audit on obtient
U1 (rev1) -> A1 (rev1)
// on modifie l'adresse (toujours du 1..N)
U1 (rev1) -> A1 (rev1)
U1 (rev1) -> A1 (rev2)
// on modifie l'utilisateur (la relation devient N..N ! deux utilisateurs pointent vers la même adresse)
U1 (rev1) -> A1 (rev1)
U1 (rev1) -> A1 (rev2)
U1 (rev3) -> A1 (rev2)
P.S : Notez bien que les objets modifiés dans une même transaction ont la même révision
Les conséquences directes sont :
- une table d'association est nécessaire entre UTILISATEUR_AUD et ADRESSE_AUD (alors que ce n'est pas le cas dans le modèle normal).
- en 1..N, le lien est porté par la table fille, on a donc dans ADRESSE une colonne qui référence l'utilisateur (UTILISATEUR_ID) ; dans la table d'audit, c'est la table d'association qui porte ce lien.
@Audited
@Entity
public class UTILISATEUR {
@Id
private Long id;
@Version
private Long version;
private String nom;
private List<String> adresses;
}
@Audited
@Entity
public class ADRESSE {
@Id
private Long id;
@Version
private Long version;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "UTILISATEUR_ID")
@AuditJoinTable(name = "UTILISATEUR_ADRESSE_AUD", inverseJoinColumns = {
@JoinColumn(name = "ADRESSE_ID")
})
private List<String> adresses
}
Je mettrais bien le DDL, mais j'ai la flemme :)
Trucs à savoir
-
évidemment Envers utilise beaucoup de ressource, attention aux performances
-
en plus des tables d'audit, il y a une table pour référencer toutes les révisions, cette table est customisable pour y ajouter des informations (utilisateur, objets, etc...)
-
il est possible d'utiliser des EJB dans les EntityTrackingRevisionListener, en faisant un lookup JNDI.(entre autre pour retrouver un EJB qui possède une variable @RequestScoped). Si j'ai le temps, je ferai une description rapide du truc.
-
Best practice : il y a des fonctions pour recharger les révisions, mais ça ne marche bien que si l'objet "chapeau" est modifié à chaque révision. Sinon c'est compliqué de retrouver l'historique à partir de l'objet chapeau. (Il suffit par exemple de positionner une date de modification) => ça n'aide pas les performances.
Le problème :
C'est galère d'installer Oracle XE sur windows 7 64 bits.
Parfois ça marche, parfois ça marche pas.
Solution 1 (non testée) :
Installer Oracle complet, disponible pour utilisation personnelle en version 64 bits.
Solution 2 :
Une erreur se produit à l'installation (fichier introuvable : KEY_XE.reg). D'après ces sites : ici et là, il faut, pendant l'installation, copier le fichier en question dans le répertoire temporaire de l'installation. Le 2ème site propose un petit .bat (key_oracle.bat) pour l'installer dans tous les répertoires temporaires, (l'installation silencieuse n'a pas marché chez moi, mais c'est basé sur des délais, donc variable suivant les environnements), à exécuter après avoir fait tous les choix, au début de l'installation proprement dite.
Plusieurs problèmes peuvent se poser après l'installation :
-
pas de base créée (répertoire C:\oraclexe\app\oracle\oradata\XE vide).
-
là c'est mort
-
en général c'est le problème du KEY_XE.reg, mais je ne suis pas sur
-
peut-être que "sys" comme mot de passe pose problème (?!?)
-
le TNS XE n'est plus reconnu (ORA-12514:No TNS Listener)
-
a priori cela se produit quand on arrête les services OracleServiceXE ou OracleXETNSListener et qu'on les redémarre,
-
ajouter la description du SID XE dans le fichier <ORACLE_HOME>/network/admin/listener.ora
(SID_DESC =
(SID_NAME = XE)
(ORACLE_HOME = c:\oraclexe\app\oracle\product\11.2.0\server)
)
-
sur expdp :ORA-12638.
-
solution ici
-
modifier le fichier <oracle home>/network/admin/sqlnet.ora
SQLNET.AUTHENTICATION_SERVICES= (NTS)
en
SQLNET.AUTHENTICATION_SERVICES= (NONE)
select dbms_xdb.gethttpport as "HTTP-Port"
, dbms_xdb.getftpport as "FTP-Port" from dual;
begin
dbms_xdb.sethttpport('80');
dbms_xdb.setftpport('2100');
end;
/
Trucs à savoir
-
le service HTTP est démarré par le service OracleXE
-
se logguer en "/ as sysdba" est compliqué (il faut que votre user soit dans un groupe ActiveDirectory précis) : à oublier
-
ne pas oublier le mot de passe SYSTEM / SYS indiqué lors de l'installation c'est la clé de tout, et comme on ne peut pas utiliser le sysdba c'est impossible à changer
-
seul deux services sont nécessaires pour la base : OracleServiceXE ou OracleXETNSListener. Et pas besoin de les mettre en démarrage automatique