[JAVA] Mappeur de connexion JDBC qui mange du SQL et renvoie des beans

introduction

Il existe encore de nombreuses sources Java qui n'ont d'autre choix que de réaliser des connexions JDBC héritées. Cela peut être l'un des problèmes des programmeurs. S'il ne s'agit que de maintenance, vous devrez peut-être en créer un nouveau.

Comme cas qui vient immédiatement à l'esprit dans le sens de l'héritage Il se présente sous la forme de "mappage des informations extraites de la base de données avec Select comme bean" Le processus de traitement est ** encore cool ** mou ...

"Conserver les résultats de la requête une fois dans ResultSet" "Tournez le curseur de ResultSet pour remplir le bean" "Boucler les beans pour implémenter la logique métier" C'est comme ça.

La conception et la définition de SQL et Bean sont correctes, mais dans le processus Il arrive souvent que le tout reste bloqué simplement en faisant une erreur imprudente.

Plus intuitivement, ** N'est-il pas complet avec juste SQL et Bean? ** Ceci est l'article.

Ce que vous voulez réaliser

Comme le dit le titre. Strictement parlant, le "type de bean retourné" doit être spécifié à l'avance. Est-il proche de ** Ma bouteille de Starba **? Lorsque vous passez une commande (SQL) et donnez ma bouteille (Bean) à l'employé Ma bouteille est remplie de café et revient.

Ce qui a été jeté dans la réalisation

Sécurité de type

Vous pouvez le voir en regardant le code qui sort après cela. Il y a une partie qui ** ignore complètement ** la "sécurité de type", qui est l'une des fonctionnalités majeures de Java. Personnellement, c'est «séparé», mais les gens qui se font prendre peuvent se faire prendre. S'il vous plaît, pardonnez-moi.

Creusement profond de la technologie ORM existante

[ORM](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83 % 88% E9% 96% A2% E4% BF% 82% E3% 83% 9E% E3% 83% 83% E3% 83% 94% E3% 83% B3% E3% 82% B0) n'est pas du tout détaillé. Donc, avec de meilleures fonctionnalités avec les intergiciels et les bibliothèques existants Il y a probablement des ressources qui font ce que je veux faire, Il y a aussi une conclusion qu'il y a de nombreuses opinions négatives plutôt que des avantages et des inconvénients de l'ORM lui-même. Bien que ce soit prématuré, j'ai arrêté de me pousser le cou de force. (Personnellement, ORM le reconnaît comme un mot ** large **, donc Je ne pense pas que tout soit mauvais C'est un fait que je déteste la chasse aux mots, alors je l'ai évité.)

Articles que j'ai utilisés comme référence

L'article suivant est le plus proche de ce que je veux faire. La source de la pièce de base est détournée presque telle quelle. [Java] Créons une bibliothèque d'accès à la base de données!

Version Java

Java 6 ou version ultérieure

Personnage

Classe JDBCManager

Classe d'infrastructure de connexion de base de données et classe qui contrôle le mappage.

Classe SqlParameter

Une classe qui gère les espaces réservés qui sont des paramètres de SQL. Il s'agit d'un ** danger de type ** qui n'est pas de type sécurisé.

SQL (chaîne de requête)

L'instruction SQL que vous souhaitez exécuter. Indépendamment de CRUD, il n'est pas nécessaire de le rendre persistant en tant que fichier ou enregistrement DB.

Classe de haricots selon Select

Classe Bean correspondant à la colonne d'extraction de Select. Créez-en un pour chaque instruction Select.

I/O Les E / S requises pour le fonctionnement minimum sont les suivantes.

contribution

-Le chemin complet de la classe du pilote JDBC (oracle.jdbc.driver.OracleDriver, etc.) -Connecteur JDBC (jdbc: oracle: thin: @ // localhost: 1521 / xe, etc.) -Utilisateur de connexion JDBC -Mot de passe de connexion JDBC ・ SQL -Paramètres SQL (cependant, non requis si les espaces réservés ne sont pas utilisés)

Production

Bean

Source de base

Il est basé sur les classes JDBCManager et SqlParameter.

Classe JDBCManager

Classe JDBCManager


import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class JDBCManager {
    private Connection conn;

    @SuppressWarnings("unused")
    private JDBCManager() throws Throwable{}

    public JDBCManager(String driver,String url,String user,String password) throws ClassNotFoundException,SQLException{
        Class.forName(driver);
        this.conn = DriverManager.getConnection(url, user, password);
        this.conn.setAutoCommit(false);
    }

    private void setParams(PreparedStatement statement, SqlParameter sqlParameter) throws SQLException {
    	if(sqlParameter == null || !sqlParameter.hasParameters()) {
    		return;
    	}
        int paramNo = 1;
        for( Object param : sqlParameter.toParameter() ) {
            statement.setObject(paramNo++, param);
        }
    }

    public void commit() throws SQLException{
        this.conn.commit();
    }

    public void rollback() throws SQLException{
        this.conn.rollback();
    }

    public void close() throws SQLException{
        this.conn.close();
    }

    private <E> E toObject(ResultSet rs, Class<E> clazz) throws InstantiationException, IllegalAccessException, SQLException {
        E bean = null;
        if( rs.next() ) {
            Field[] fields = clazz.getDeclaredFields();
            bean = clazz.newInstance();
            for (Field f : fields) {
                f.setAccessible(true);
                Object val = rs.getObject(f.getName());
                f.set(bean, val);
            }
        }
        return bean;
    }

    private <E> List<E> toObjectList(ResultSet rs, Class<E> clazz) throws SQLException, InstantiationException, IllegalAccessException  {
        List<E> rsList = new ArrayList<E>();
        while (rs.next()) {
            Field[] fields = clazz.getDeclaredFields();
            E bean = clazz.newInstance();
            for( Field f: fields ) {
                f.setAccessible(true);
                Object val = rs.getObject( f.getName() );
                f.set(bean, val);
            }
            rsList.add(bean);
        }
        return rsList;
    }

    private SQLException sqlErr(Throwable e, CharSequence sql) {
        return new SQLException("sql error!\nerror occurred sql="+sql, e);
    }


    public int insert(String sql, SqlParameter sqlParameter) throws SQLException {
    	return exert(sql,sqlParameter);
    }

    public int update(String sql, SqlParameter sqlParameter) throws SQLException {
    	return exert(sql,sqlParameter);
    }

    public int delete(String sql, SqlParameter sqlParameter) throws SQLException {
    	return exert(sql,sqlParameter);
    }

    private int exert(String sql, SqlParameter sqlParameter) throws SQLException {
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(sql.toString());
            setParams(statement,sqlParameter);
            int ret = statement.executeUpdate();
            return ret;
        }catch (SQLException e){
            throw sqlErr(e, sql);
        }finally {
            try {
                if(statement != null){
                    statement.close();
                }
            } catch (SQLException e) {
            	e.printStackTrace();
            }
        }
    }

    public <E> E selectOne(String sql, Class<E> clazz) throws SQLException {
    	return selectOne(sql,clazz,null);
    }

    public <E> E selectOne(String sql, Class<E> clazz, SqlParameter sqlParameter) throws SQLException {
        ResultSet rs = null;
        PreparedStatement statement = null;
        try{
            statement = conn.prepareStatement(sql.toString());
            setParams(statement,sqlParameter);
            rs = statement.executeQuery();

            E val = toObject(rs, clazz);
            return val;
        }catch(Exception e) {
            throw sqlErr(e, sql);
        }finally {
            try {
                if( rs != null ) {
                    rs.close();
                }
            }catch(SQLException e) {
            	e.printStackTrace();
            }finally {
            	try {
                    if(statement != null){
                        statement.close();
                    }
            	}catch(SQLException e) {
            		e.printStackTrace();
            	}
            }
        }
    }

    public <E> List<E> selectList(String sql, Class<E> clazz) throws SQLException {
    	return selectList(sql,clazz,null);
    }

    public <E> List<E> selectList(String sql, Class<E> clazz, SqlParameter sqlParameter) throws SQLException {
        ResultSet rs = null;
        PreparedStatement statement = null;
        try {
            statement = conn.prepareStatement(sql.toString());
            setParams(statement,sqlParameter);
            rs = statement.executeQuery();
            List<E> rsList = toObjectList(rs, clazz);
            return rsList;
        }catch(Exception e) {
            throw sqlErr(e, sql);
        }finally {
            try {
                if( rs != null ) {
                    rs.close();
                }
            }catch(SQLException e) {
            	e.printStackTrace();
            }finally {
            	try {
                    if(statement != null){
                        statement.close();
                    }
            	}catch(SQLException e) {
            		e.printStackTrace();
            	}
            }
        }
    }
}

Classe SqlParameter

Classe SqlParameter


import java.util.ArrayList;
import java.util.List;

public class SqlParameter {
    @SuppressWarnings("rawtypes")
    private List list = null;

    @SuppressWarnings("rawtypes")
    public SqlParameter(int paramNumber){
        list = new ArrayList(paramNumber);
    }

    @SuppressWarnings("rawtypes")
	public SqlParameter(){
    	list = new ArrayList();
    }

    @SuppressWarnings("unchecked")
    public <E> void addParameter(E e){
        list.add(e);
    }

    public boolean hasParameters() {
    	return list.size()==0 ? false : true;
    }

    public Object[] toParameter(){
        return list.toArray();
    }
}

Exemple d'opération

Je vais le proposer dans quatre cas. La base de données cible est "Oracle (Express Edition)". Les cibles de l'opération sont les tables "EMP" et "DEPT" qui existent par défaut dans le schéma.

(1) Sélectionnez: SQL d'acquisition multiple sans clause WHERE (aucun espace réservé requis) (2) Sélectionnez: Obtenir plusieurs enregistrements avec clause WHERE (avec espace réservé) SQL ③ Sélectionnez: SQL d'acquisition unique ④ Mettre à jour (insérer, mettre à jour, supprimer)

Nous allons créer des beans en fonction de chaque SQL. Le but est de rendre le ** alias (AS) de Select et le nom de champ du bean identiques **.

Déclarez également le type de champ bean dans une classe selon la spécification JDBC.

Dans Select of this sample, nous opérons sur une seule table. Le même processus peut être effectué avec Select pour plusieurs tables en utilisant ** join **.

Le point est "** C'est bien si SQL et Bean correspondent **".

(1) Sélectionnez: SQL d'acquisition multiple sans clause WHERE (aucun espace réservé requis)

Select_SQL d'acquisition multiple sans clause WHERE (aucun espace réservé requis)


SELECT 
	--VARCHAR2
	DNAME AS dname 
FROM 
	DEPT

Classe DeptBean


public class DeptBean {
	private String dname = null;

	public String getDname() {
		return dname;
	}

	public void setDname(String dname) {
		this.dname = dname;
	}
}

(2) Sélectionnez: Obtenir plusieurs enregistrements avec clause WHERE (avec espace réservé) SQL

Select_Obtenir plusieurs enregistrements avec clause WHERE (avec espace réservé) SQL


SELECT 
	 --NUMBER
	 EMPNO AS empNo, 
	 --VARCHAR2
	 ENAME AS ename, 
	 --DATE
	 HIREDATE AS hireDate 
 FROM 
	 EMP 
 WHERE 
	 DEPTNO = ? 
	 AND 
	 JOB = ? 

Classe EmpBean


import java.math.BigDecimal;
import java.sql.Timestamp;

public class EmpBean {
	private BigDecimal empNo = null;
	private String ename = null;
	private Timestamp hireDate = null;

	public BigDecimal getEmpNo() {
		return empNo;
	}
	public void setEmpNo(BigDecimal empNo) {
		this.empNo = empNo;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public Timestamp getHireDate() {
		return hireDate;
	}
	public void setHireDate(Timestamp hireDate) {
		this.hireDate = hireDate;
	}
}

③ Sélectionnez: SQL d'acquisition unique

Select_SQL de capture unique


SELECT 
	 --NUMBER
	 EMPNO AS empNo, 
	 --VARCHAR2
	 ENAME AS ename, 
	 --DATE
	 HIREDATE AS hireDate 
 FROM 
	 EMP 
 WHERE 
	//PK
	 EMPNO = ?

Classe EmpBean


(Comme c'est le même que ci-dessus, il est omis)

④ Mettre à jour (insérer, mettre à jour, supprimer)

Utilisons insert comme exemple. Vous n'avez pas besoin de créer un bean pour la mise à jour car le résultat est reçu en tant que int.

insert


INSERT INTO DEPT 
	 --NUMBER,VARCHAR2,VARCHAR2
	 (DEPTNO, DNAME, LOC) 
	 values 
	 (?, ?, ?) 

Classe d'essai

Je vais déplacer les quatre cas ci-dessus.

Classe d'essai


import java.math.BigDecimal;
import java.util.List;

public class JDBCTest {
	public static void main(String[] args) throws Throwable{
		JDBCTest t = new JDBCTest();
		t.execute();
	}

	public void execute() throws Throwable{
		//Informations de connexion JDBC
		String driver = "oracle.jdbc.driver.OracleDriver";
		String url = "jdbc:oracle:thin:@//localhost:1521/xe";
		String user = "ouser";
		String password = "ouser";
		//Initialiser le gestionnaire JDBC
		JDBCManager jdbc = new JDBCManager(driver,url,user,password);

		//Modèle 1. SQL d'acquisition multiple sans clause WHERE (aucun espace réservé requis)
		String sql = "SELECT "
						+ "DNAME AS dname "
					+ "FROM "
						+ "DEPT ";
		//Obtenir la liste
		List<DeptBean> dlist = jdbc.selectList(sql, DeptBean.class);

		System.out.println("――― Modèle 1 ―――");
		for(DeptBean d : dlist) {
			System.out.println(d.getDname());
		}

		//Modèle 2. Obtenir plusieurs enregistrements avec clause WHERE (avec espace réservé) SQL
		//Ajuster au champ bean avec alias
		sql =		"SELECT "
						+ "EMPNO AS empNo, "
						+ "ENAME AS ename, "
						+ "HIREDATE AS hireDate "
					+ "FROM "
						+ "EMP "
					+ "WHERE "
						+ "DEPTNO = ? "
						+ "AND "
						+ "JOB = ? ";

		//Deux espaces réservés
		SqlParameter p = new SqlParameter(2);
		//Lier dans l'ordre depuis le début
		p.addParameter(new BigDecimal(30));
		p.addParameter("SALESMAN");

		//Obtenir la liste
		List<EmpBean> elist = jdbc.selectList(sql, EmpBean.class, p);

		System.out.println("――― Modèle 2 ―――");
		for(EmpBean e : elist) {
			System.out.println(e.getEmpNo() + "\t" + e.getEname() + "\t" + e.getHireDate());
		}

		//Modèle 3. SQL de capture unique
		sql =		"SELECT "
						+ "EMPNO AS empNo, "
						+ "ENAME AS ename, "
						+ "HIREDATE AS hireDate "
					+ "FROM "
						+ "EMP "
					+ "WHERE "
						//PK
						+ "EMPNO = ? ";
		p = new SqlParameter(1);
		p.addParameter(new BigDecimal(7369));

		//Obtenez Bean
		EmpBean emp = jdbc.selectOne(sql, EmpBean.class, p);
		System.out.println("――― Modèle 3 ―――");
		System.out.println(emp.getEname());

		//Modèle 4. Mettre à jour (insérer,update,delete)
		sql =		"INSERT INTO DEPT "
						+ "(DEPTNO, DNAME, LOC) "
						+ "values "
						+ "(?, ?, ?) ";

		p = new SqlParameter(3);
		p.addParameter(new BigDecimal(99));
		p.addParameter("SPECIAL");
		p.addParameter("JAPAN");

		int i = jdbc.insert(sql, p);
		System.out.println("――― Modèle 4 ―――");
		System.out.println("Le nombre d'insertions"+i+"C'est une question");
		jdbc.commit();
		//En fait, intercepter une erreur et attraper jdbc.rollback()Courir

		jdbc.close();
		//En fait, il piège une erreur et enfin jdbc.close()Courir
	}
}

Le résultat de l'exécution est le suivant.

Résultat d'exécution (sortie standard)


――― Modèle 1 ―――
ACCOUNTING
RESEARCH
SALES
OPERATIONS
――― Modèle 2 ―――
7499	ALLEN	1981-02-20 00:00:00.0
7521	WARD	1981-02-22 00:00:00.0
7654	MARTIN	1981-09-28 00:00:00.0
7844	TURNER	1981-09-08 00:00:00.0
――― Modèle 3 ―――
SMITH
――― Modèle 4 ―――
Le nombre d'insertions est de 1.

Résumé

Même si vous comparez à partir de l'article auquel j'ai fait référence Il n'y a presque pas de nouveau contenu, Le risque de provoquer une erreur d'exécution est augmenté car il n'est pas de type sécurisé. Personnellement, je gère au plus trois cibles, ** SQL **, ** paramètres ** et ** Bean **. S'il s'agit de jouer avec Resultset et de prendre du temps Je pense que cela vaut le compromis pour la sécurité de type.

De plus, dans un environnement où Java ne peut pas être mis à niveau pour diverses raisons, Dans un environnement où le middleware basé sur la base de données et les bibliothèques telles que ORM ne peuvent pas être installés, Même s'il y a des restrictions environnementales et opérationnelles J'aime le fait qu'il ne peut fonctionner qu'avec les anciennes VM Java **.

Cette fois, j'ai ciblé Oracle comme base de données Si vous pouvez vous connecter avec JDBC, il existe des différences de comportement dialectiques. Je suis optimiste que n'importe quelle base de données peut être utilisée.

Recommended Posts

Mappeur de connexion JDBC qui mange du SQL et renvoie des beans
[À propos de JDBC qui connecte Java et SQL]
Connexion Java-base de données Connexion Java-MySQL ①: Présentation du pilote JDBC et JDBC / Septembre 2017