[JAVA] JDBC-Verbindungs-Mapper, der SQL frisst und Beans zurückgibt

Einführung

Es gibt immer noch viele Java-Quellen, die keine andere Wahl haben, als ältere JDBC-Verbindungen zu realisieren. Dies kann eines der Probleme für Programmierer sein. Wenn es sich nur um Wartung handelt, müssen Sie möglicherweise eine neue erstellen.

Als ein Fall, der sofort im Sinne eines Vermächtnisses in den Sinn kommt Es hat die Form "Zuordnung der aus der Datenbank gezogenen Informationen mit Select als Bean". Der Verarbeitungsprozess ist ** noch cool ** locker ...

"Abfrageergebnisse einmal in ResultSet speichern" "Drehen Sie den Cursor von ResultSet, um die Bean zu füllen" "Looping Beans zur Implementierung von Geschäftslogik" Es ist wie es ist.

Das Design und die Definition von SQL und Bean sind korrekt, aber dabei Es ist oft der Fall, dass das Ganze nur durch einen unachtsamen Fehler hängen bleibt.

Intuitiver: ** Ist es nicht komplett mit SQL und Bean? ** Dies ist der Artikel.

Was Sie erreichen wollen

Wie der Titel schon sagt. Genau genommen muss der "zurückgegebene Bohnen-Typ" im Voraus angegeben werden. Ist es in der Nähe von ** My Bottle of Starba **? Wenn Sie eine Bestellung aufgeben (SQL) und dem Angestellten meine Flasche (Bean) geben Meine Flasche ist mit Kaffee gefüllt und kehrt zurück.

Was in der Verwirklichung weggeworfen wurde

Typensicherheit

Sie können es sehen, indem Sie sich den Code ansehen, der danach herauskommt. Es gibt einen Teil, der die "Typensicherheit", die eines der Hauptmerkmale von Java ist, völlig ignoriert. Persönlich ist es "getrennt", aber Menschen, die erwischt werden, können erwischt werden. Bitte verzeih mir.

Tiefes Graben der vorhandenen ORM-Technologie

[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) ist überhaupt nicht detailliert. Also mit besserer Funktionalität mit vorhandener Middleware und Bibliotheken Es gibt wahrscheinlich Ressourcen, die das tun, was ich tun möchte. Es gibt auch einen Schlusspunkt, dass es viele negative Meinungen gibt und nicht Vor- und Nachteile von ORM selbst. Obwohl es verfrüht ist, habe ich aufgehört, meinen Hals gewaltsam zu stoßen. (ORM erkennt es persönlich als ein ** breites ** Wort, also Ich denke nicht, dass alles schlecht ist Es ist eine Tatsache, dass ich die Wortjagd hasse, also habe ich sie vermieden.)

Artikel, die ich als Referenz verwendet habe

Der folgende Artikel kommt dem, was ich tun möchte, am nächsten. Die Quelle des Basisteils wird fast so wie sie ist umgeleitet. [Java] Erstellen wir eine DB-Zugriffsbibliothek!

Java-Version

Java 6 oder höher

Charakter

JDBCManager-Klasse

DB-Verbindungsinfrastrukturklasse und Klasse, die die Zuordnung steuert.

SqlParameter-Klasse

Eine Klasse, die Platzhalter verwaltet, die Parameter für SQL sind. Dies ist eine ** Typgefahr **, die nicht typsicher ist.

SQL (Abfragezeichenfolge)

Die SQL-Anweisung, die Sie ausführen möchten. Unabhängig von CRUD muss es nicht als Datei oder DB-Datensatz persistent gemacht werden.

Bohnenklasse nach Select

Bean-Klasse, die der Extraktionsspalte von Select entspricht. Erstellen Sie eine für jede Select-Anweisung.

I/O Die für den Mindestbetrieb erforderliche E / A ist wie folgt.

Eingang

Ausgabe

Bean

Basisquelle

Es basiert auf den Klassen JDBCManager und SqlParameter.

JDBCManager-Klasse

JDBCManager-Klasse


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();
            	}
            }
        }
    }
}

SqlParameter-Klasse

SqlParameter-Klasse


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();
    }
}

Betriebsbeispiel

Ich werde es in vier Fällen verschieben. Die Ziel-DB ist "Oracle (Express Edition)". Die Operationsziele sind die Tabellen "EMP" und "DEPT", die standardmäßig im Schema vorhanden sind.

(1) Wählen Sie: SQL mit Mehrfacherfassung ohne WHERE-Klausel (kein Platzhalter erforderlich) (2) Wählen Sie: Mehrere Datensätze mit der WHERE-Klausel (mit Platzhalter) SQL abrufen ③ Wählen Sie: Single Acquisition SQL ④ Aktualisieren (einfügen, aktualisieren, löschen)

Wir werden Beans gemäß jedem SQL erstellen. Der Punkt ist, den ** Alias (AS) von Select und den Feldnamen der Bean gleich zu machen **.

Deklarieren Sie außerdem den Bean-Feldtyp in einer Klasse gemäß der JDBC-Spezifikation.

In Auswahl dieses Beispiels arbeiten wir mit einer einzelnen Tabelle. Der gleiche Vorgang kann mit Select für mehrere Tabellen mit ** join ** ausgeführt werden.

Der Punkt ist "** Es ist gut, wenn SQL und Bean übereinstimmen **".

(1) Wählen Sie: SQL mit Mehrfacherfassung ohne WHERE-Klausel (kein Platzhalter erforderlich)

Select_Mehrfacherfassungs-SQL ohne WHERE-Klausel (kein Platzhalter erforderlich)


SELECT 
	--VARCHAR2
	DNAME AS dname 
FROM 
	DEPT

DeptBean-Klasse


public class DeptBean {
	private String dname = null;

	public String getDname() {
		return dname;
	}

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

(2) Wählen Sie: Mehrere Datensätze mit der WHERE-Klausel (mit Platzhalter) SQL abrufen

Select_Holen Sie sich mehrere Datensätze mit der WHERE-Klausel (mit Platzhalter) SQL


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

EmpBean-Klasse


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;
	}
}

③ Wählen Sie: Single Acquisition SQL

Select_Single Capture SQL


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

EmpBean-Klasse


(Da es das gleiche wie oben ist, wird es weggelassen)

④ Aktualisieren (einfügen, aktualisieren, löschen)

Verwenden wir Insert als Beispiel. Sie müssen keine Bean zum Aktualisieren erstellen, da das Ergebnis als int empfangen wird.

insert


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

Testklasse

Ich werde alle oben genannten vier Fälle verschieben.

Testklasse


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{
		//JDBC-Verbindungsinformationen
		String driver = "oracle.jdbc.driver.OracleDriver";
		String url = "jdbc:oracle:thin:@//localhost:1521/xe";
		String user = "ouser";
		String password = "ouser";
		//JDBC-Manager initialisieren
		JDBCManager jdbc = new JDBCManager(driver,url,user,password);

		//Muster 1. Mehrfacherfassungs-SQL ohne WHERE-Klausel (kein Platzhalter erforderlich)
		String sql = "SELECT "
						+ "DNAME AS dname "
					+ "FROM "
						+ "DEPT ";
		//Liste abrufen
		List<DeptBean> dlist = jdbc.selectList(sql, DeptBean.class);

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

		//Muster 2. Holen Sie sich mehrere Datensätze mit der WHERE-Klausel (mit Platzhalter) SQL
		//Mit Alias an das Bohnenfeld anpassen
		sql =		"SELECT "
						+ "EMPNO AS empNo, "
						+ "ENAME AS ename, "
						+ "HIREDATE AS hireDate "
					+ "FROM "
						+ "EMP "
					+ "WHERE "
						+ "DEPTNO = ? "
						+ "AND "
						+ "JOB = ? ";

		//Zwei Platzhalter
		SqlParameter p = new SqlParameter(2);
		//Von Anfang an in der richtigen Reihenfolge binden
		p.addParameter(new BigDecimal(30));
		p.addParameter("SALESMAN");

		//Liste abrufen
		List<EmpBean> elist = jdbc.selectList(sql, EmpBean.class, p);

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

		//Muster 3. Single Capture SQL
		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));

		//Holen Sie sich Bean
		EmpBean emp = jdbc.selectOne(sql, EmpBean.class, p);
		System.out.println("――― Muster 3 ―――");
		System.out.println(emp.getEname());

		//Muster 4. Update (einfügen,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("――― Muster 4 ―――");
		System.out.println("Die Anzahl der Einfügungen"+i+"Es ist eine Sache");
		jdbc.commit();
		//Tatsächlich fängt es einen Fehler ein und fängt jdbc ab.rollback()Lauf

		jdbc.close();
		//Eigentlich fängt es einen Fehler und schließlich jdbc.close()Lauf
	}
}

Das Ausführungsergebnis ist wie folgt.

Ausführungsergebnis (Standardausgabe)


――― Muster 1 ―――
ACCOUNTING
RESEARCH
SALES
OPERATIONS
――― Muster 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
――― Muster 3 ―――
SMITH
――― Muster 4 ―――
Die Anzahl der Einfügungen beträgt 1.

Zusammenfassung

Auch wenn Sie mit dem Artikel vergleichen, auf den ich mich bezog Es gibt fast keine neuen Inhalte, Das Risiko, einen Laufzeitfehler zu verursachen, ist erhöht, da er nicht typsicher ist. Persönlich behandle ich höchstens drei Ziele, ** SQL **, ** Parameter ** und ** Bean **. Wenn es darum geht, die Ergebnismenge durcheinander zu bringen und sich Zeit zu nehmen Ich denke, es ist den Kompromiss für die Typensicherheit wert.

In einer Umgebung, in der Java aus verschiedenen Gründen nicht aktualisiert werden kann, In einer Umgebung, in der DB-basierte Middleware und Bibliotheken wie ORM nicht installiert werden können, Auch wenn es Umwelt- und Betriebsbeschränkungen gibt Ich mag die Tatsache, dass es nur mit älteren Java-VMs ** funktionieren kann.

Dieses Mal habe ich Oracle als Datenbank ausgewählt Wenn Sie eine Verbindung mit JDBC herstellen können, gibt es einige dialektische Unterschiede im Verhalten. Ich bin optimistisch, dass jede Datenbank verwendet werden kann.

Recommended Posts

JDBC-Verbindungs-Mapper, der SQL frisst und Beans zurückgibt
[Über JDBC, das Java und SQL verbindet]
Java-Datenbankverbindung Java-MySQL-Verbindung ①: Übersicht über JDBC und JDBC-Treiber / September 2017