サンプルDB:hanbai.sql
全件参照:SELECT 列名 FROM テーブル名
例:SELECT * FROM shouhin
抽出:SELECT 列名 FROM テーブル名 WHERE 抽出条件
※抽出条件:sid=1 など。LIKE で文字列の部分検索可能(例:snameが「り」で始まる rsname LIKE "り%")
例:SELECT * FROM shouhin WHERE sid=1
整列:SELECT 列名 FROM テーブル名 ORDER BY 整列順
※整列順:列名 昇順(ASC) or 降順(DESC)
例:SELECT * FROM shouhin ORDER BY sid DESC
追加:INSERT INTO テーブル名(列名1,列名2,列名3,,) VALUES(値1,値2,値3・・)
例:INSERT INTO shouhin (sname,tanka) VALUES('もも',100)
変更:UPDATE テーブル名 SET 列名=値,,, WHERE 抽出条件
例:UPDATE shouhin SET sname='かき',tanka=200 WHERE sid=4
削除:DELETE FROM テーブル名 WHERE 抽出条件
例:DELETE FROM shouhin WHERE sid=5
JDBC(Java Database Connectivity)はJavaからデータベースを使うための仕組み。異なるDBMSも同じソースコードで扱うことが出来る。ただし、各DBMS用のJDBCドライバを用意する必要がある。
| クラス名 | 役割 |
|---|---|
| DriverManager | Connectionの取得 |
| Connection | データベースとの接続 |
| PreparedStatement | SQLの実行 |
| ResultSet | SELECTでの結果を格納 |
| SqlException | データベース処理の例外クラス |
データベースに接続し、Connectionクラスのオブジェクトを取得する。
Connection con = DriverManager.getConnection (データソース, ユーザ名, パスワード);
データソースの書き方はデータベースによって異なる。
//同じPC(localhost)のhanbaiデータベースに接続する場合 "jdbc:mysql://localhost/hanbai"
あらかじめ、クラスのフィールド(定数)としてデータソース、ユーザ名、パスワードを指定しておく。
static final String SRC = "jdbc:mysql://localhost/hanbai"; static final String USER = "java"; static final String PASS = "pass";
メソッド内に以下の接続のコードを書く。
Connection con = DriverManager.getConnection(SRC,USER,PASS);
※java.sql.*をインポートする。
※SQLExceptionの例外処理が必要。以降のDB処理も同様。
接続完了後、SQLを実行するなどの処理を行う。
最後に、con.close()のようにして接続を切断し、処理を終了する。
try with resource文を使うと自動的にcloseされる。
public class DbSample {
static final String SRC = "jdbc:mysql://localhost/hanbai";
static final String USER = "java";
static final String PASS = "pass";
public static void main(String[] args) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
// テスト用に現在のデータベースを表示
System.out.println(con.getCatalog());
} catch (SQLException e) {
System.out.println("DB接続エラー:" + e.getMessage());
}
}
}
SQLを実行するためには、まずその準備を行うためのPreparedStatementクラスのオブジェクトを取得する。
ConnectionオブジェクトのprepareStatementメソッドで、引数にSQL文字列を指定することで、PreparedStatementクラスのオブジェクトを取得する。
PreparedStatement stmt = con.prepareStatement(SQL文字列);
//例:
String sql = "INSERT INTO shouhin (sname,tanka) VALUES('もも',100)";
PreparedStatement stmt = con.prepareStatement(sql);
PrepareStatementオブジェクトを得ても、SQL実行の準備をしただけで、SQL自体はまだ実行されていない。
INSERT、DELETE、UPDATEなど更新系のSQLの実行は、PrepareStatementクラスの executeUpdateメソッドを使用する。
//例 stmt.executeUpdate();
※executeUpdateは戻り値として更新があった行数をint型で返す。INSERTが正常に終了した場合、戻り値は1になる。追加できなかった場合、0になる。DELETEは削除した件数、UPDATEは変更した件数になる。
※Connectionは自動close使用
public class DbSample {
static final String SRC = "jdbc:mysql://localhost/hanbai";
static final String USER = "java";
static final String PASS = "pass";
public static void main(String[] args) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "INSERT INTO shouhin (sname,tanka) VALUES('もも',100)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.executeUpdate();
}catch (SQLException e) {
System.out.println("INSERTエラー:" + e.getMessage());
}
}
}
insert() メソッドを作り、mainから呼び出すようにした例。
public class DbSample {
static final String SRC = "jdbc:mysql://localhost/hanbai";
static final String USER = "java";
static final String PASS = "pass";
public static void main(String[] args) {
insert();
}
public static void insert() {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "INSERT INTO shouhin (sname,tanka) VALUES('もも',100)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.executeUpdate();
}catch (SQLException e) {
System.out.println("INSERTエラー:" + e.getMessage());
}
}
}
SELECT文の実行も基本は同じだが、executeUpdate ではなくexecuteQueryを行う。
戻り値はResultSetクラスのオブジェクトであり、これに検索結果が入っている。
ResultSet rs = stmt.executeQuery();
ResultSetクラスのオブジェクトから結果を取り出すことができる。
結果を一件(一行)取り出すには まずnextメソッドを使用する。これで最初の行を扱うことが出来る。
rs.next();現在の行の各列を取り出すには、文字列の場合 getStringメソッド、整数の場合、 getIntメソッド を使用し、列名を指定する(他に、getLong、getDouble、getDate、getTimestamp、getBoolean など、取得する型に応じたメソッドがある。LocalDateはgetDate("列名").toLocalDate())。
String sname = rs.getString("sname");
int tanka = rs.getInt("tanka");
System.out.println(sname + " " + tanka);
さらにnextメソッドを使うと、次の行の結果を取得できる。
次の行が無いときには、nextメソッドの戻り値がfalseになる。
全データを取得する場合 while文でnext メソッドを使い、false を得たところで終了する。
while( rs.next() ) {
String sname = rs.getString("sname");
int tanka = rs.getInt("tanka");
System.out.println(sname + " " + tanka);
}
public static void select() {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "SELECT * FROM shouhin";
PreparedStatement stmt = con.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
int sid = rs.getInt("sid");
String sname = rs.getString("sname");
int tanka = rs.getInt("tanka");
System.out.println(sid + " " + sname + " " + tanka);
}
}catch (SQLException e) {
System.out.println("SELECTエラー:" + e.getMessage());
}
}
| MySQL | Java |
|---|---|
| VARCHAR/CHAR/TEXT | String |
| INT/INTEGER | int |
| BIGINT | long |
| TINYINT(0=false,1=true) | boolean |
| DOUBLE | double |
| DATE | Date(java.sql) |
| TIME | Time |
| DATETIME/TIMESTAMP | Timestamp |
MySQLのDATE型に対応。java.util.Dateとは違い、日付のみを扱う(時間は扱わない)。コンストラクタの引数はエポックタイム(new Date()は出来ない)。
// 文字列から
Date d = Date.valueOf("2020-03-05");
// 特定の日付 Calendar c = Calendar.getInstance(); c.set(2020, 9,1); // 2020年10月1日 Date d = new Date(c.getTimeInMillis());
// 今日の日付 Date d = new Date(System.currentTimeMillis());
// LocalDateへの変換 LocalDate localDate = d.toLocalDate(); // LocalDateからの変換 Date d = Date.valueOf(localDate);
MySQLのDATETIME型に対応。日時(日付と時間)を扱う。コンストラクタの引数はエポックタイム。
// 文字列から
Timestamp ts = Timestamp.valueOf("2022-04-24 9:59:10");
// 現在日時 Timestamp ts = new Timestamp(System.currentTimeMillis());
// LocalDateTimeへの変換 LocalDateTime localDateTime = ts.toLocalDateTime(); // LocalDateTimeからの変換 Timestamp ts = Timestamp.valueOf(localDateTime);
MySQLのTIME型に対応。時間のみを扱う。コンストラクタの引数はエポックタイム。
// 文字列から
Time t = Time.valueOf("13:59:10");
// 現在日時 Times t = new Time(System.currentTimeMillis());
// LocalTimeへの変換 LocalTime localTime = t.toLocalTime(); // LocalTimeからの変換 Time t = Time.valueOf(localTime);
単純に文字列を連結し、SQLを発行する形のプログラムはセキュリティ上「SQLインジェクション」の危険がある。
これを避けるにはプレースホルダ(SQL内の場所を確保する記号)の利用を行う。
値の部分をプレースホルダである「 ? 」に書き換える。なお、値が文字列でも ? を ''で囲む必要は無い。
String sql = "INSERT INTO shouhin (sname,tanka) VALUES(?, ?)"; PreparedStatement stmt = con.prepareStatement(sql);
PreparedStatementオブジェクトを作成後、そのメソッドを使い、?に入れるデータを指定する。
String型はsetStringメソッド、int型はsetIntで指定する(他に、setLong、setDouble、setDate、setTimestamp などデータ型に応じたメソッドがある。LocalDateの場合、setDate(Date.valueOf(値) )。
引数は?の位置と値を指定する。
stmt.setString(1, sname); stmt.setInt(2, tanka);
オブジェクト.setString(番号, 値); 番号:?の出現位置(左から1、2、3・・) 例:INSERT INTO shouhin (sname,tanka) VALUES(?, ?)の場合 INSERT INTO shouhin (sname,tanka) VALUES(1番, 2番) となる 値:埋め込みたいデータ
public static void insert(String sname, int tanka) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "INSERT INTO shouhin (sname,tanka) VALUES(?, ?)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, sname);
stmt.setInt(2, tanka);
stmt.executeUpdate();
}catch (SQLException e) {
System.out.println("INSERTエラー:" + e.getMessage());
}
}
データベースを簡単に使用できるようにクラスにまとめる典型的パターン(デザインパターンの1つ)。
DTO は Data Transfer Object(データ転送用オブジェクト) で1行分のデータを入れる入れ物のクラス。
DAO は Data Access Object でDTOを使ってデータを操作するメソッドを集めたクラス。
プログラムは必ずDAOを介してデータベースを使うようにする。そうすることで、データベースを使うコードをプログラム本体から分離できる。
データ1件(1行)を表すクラスをDTOクラスという(エンティティとも呼ぶ)。テーブルの各列をフィールドにしてクラス化する。
public class Shouhin {
// フィールド テーブルの各列
private int sid;
private String sname;
private int tanka;
// コンストラクタ(eclipseで生成: [ソース]-[フィールドを使用してコンストラクタ生成])
// getter/setter(eclipseで生成: [ソース]-[getterおよびsetterの生成])
// デバッグ用にtoStringもeclipse生成すると便利([ソース]-[toString生成])
}
データベースの操作を実現するクラス。外部とのやりとり(引数や戻り値)に原則としてDTOクラスを使う。
一般に次のようなメソッドを備える。
public class ShouhinDAO {
private final String SRC = "jdbc:mysql://localhost/hanbai";
private final String USER = "java";
private final String PASS = "pass";
public void insert(Shouhin dto) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "INSERT INTO shouhin (sname,tanka) VALUES(?,?)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, dto.getSname());
stmt.setInt(2, dto.getTanka());
stmt.executeUpdate();
}
catch (SQLException e) {
System.out.println("INSERTエラー:" + e.getMessage());
}
}
public void update(Shouhin dto) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "UPDATE shouhin SET sname=?,tanka=? WHERE sid=?";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1,dto.getSname());
stmt.setInt(2, dto.getTanka());
stmt.setInt(3, dto.getSid());
stmt.executeUpdate();
}
catch (SQLException e) {
System.out.println("UPDATE エラー:" + e.getMessage());
}
}
public void delete(int sid) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "DELETE FROM shouhin WHERE sid=?";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setInt(1, sid);
stmt.executeUpdate();
}
catch (SQLException e) {
System.out.println("DELETE エラー:" + e.getMessage());
}
}
public List<Shouhin> findAll(){
List<Shouhin> list = new ArrayList<>();
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "SELECT * FROM shouhin";
PreparedStatement stmt = con.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Shouhin dto = new Shouhin(
rs.getInt("sid"),
rs.getString("sname"),
rs.getInt("tanka")
);
list.add(dto);
}
}
catch (SQLException e) {
System.out.println("findAllエラー:" + e.getMessage());
}
return list;
}
public Shouhin find(int sid){
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
String sql = "SELECT * FROM shouhin WHERE sid = ?";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setInt(1, sid);
ResultSet rs = stmt.executeQuery();
if( rs.next() ){
Shouhin dto = new Shouhin(
rs.getInt("sid"),
rs.getString("sname"),
rs.getInt("tanka")
);
return dto;
}
}
catch (SQLException e) {
System.out.println("findエラー:" + e.getMessage());
}
return null;
}
}
コントローラなどで利用する
ShouhinDAO dao = new ShouhinDAO(); // 追加時にsidは必要ないので0 Shouhin dto = new Shouhin(0,"ドリアン",800); dao.insert(dto);
ShouhinDAO dao = new ShouhinDAO(); Shouhin dto = new Shouhin(4,"かき",200); dao.update(dto);
ShouhinDAO dao = new ShouhinDAO(); // sid が 5 のものを削除 dao.delete(5);
ShouhinDAO dao = new ShouhinDAO(); List<Shouhin> list = dao.findAll();
ShouhinDAO dao = new ShouhinDAO(); Shouhin dto = dao.find(1);
結合してデータを取得したい場合、次の方法がある。
public class Uriage {
private int uid;
private int sid;
private int mid;
private int kosu;
private Date hi;
private Shouhin shouhin;
// コンストラクタはそのまま
public Uriage(int uid, int sid, int kosu, Date hi) {
this.uid = uid;
this.sid = sid;
this.kosu = kosu;
this.hi = hi;
}
// 以下getter/setter
// shouhinのgetter/setterも作成
public Shouhin getShouhin() {
return shouhin;
}
public void setShouhin(Shouhin shouhin) {
this.shouhin = shouhin;
}
}
DAOでは、findAllなどUriageを検索するところで結合のSQLを実行し、Uriageインスタンス生成時にShouhinインスタンスも生成し、Uriageにセットする。
String sql = "SELECT * FROM uriage JOIN shouhin ON uriage.sid = shouhin.sid";
PreparedStatement stmt = con.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
while(rs.next()) {
Uriage dto = new Uriage(
rs.getInt("uid"),
rs.getInt("sid"),
rs.getInt("mid"),
rs.getInt("kosu"),
rs.getDate("hi")
);
// Shouhinインスタンス生成
Shouhin s = new Shouhin(
rs.getInt("sid"),
rs.getString("sname"),
rs.getInt("tanka")
);
// ShouhinをUriageにセット
dto.setShouhin(s);
list.add(dto);
}
JSPでは、Uriageインスタンスの中のshouhinインスタンスのsnameやtankaを表示する。
<td>${u.uid}</td>
<td>${u.sid}</td>
<td>${u.shouhin.sname}</td>
<td>${u.shouhin.tanka}円</td>
INSERT直後に auto_increment で自動挿入された主キーは、MySQLの場合、以下のSQLで取得できる。
SELECT last_insert_id()
ただし、コネクションを閉じてしまうと取得できない。よってinsertと連続して取得する。
例:DAOで、insertで挿入したキーを戻り値として返す。
public int insert(Shouhin dto) {
try(Connection con = DriverManager.getConnection(SRC,USER,PASS)){
// 通常の追加処理
String sql = "INSERT INTO shouhin (sname,tanka) VALUES(?,?)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, dto.getSname());
stmt.setInt(2, dto.getTanka());
stmt.executeUpdate();
// ID取得
sql = "SELECT last_insert_id()";
PreparedStatement stmt2 = con.prepareStatement(sql);
ResultSet rs = stmt2.executeQuery();
if( rs.next() ){
int index = rs.getInt(1);
return index;
}
}
catch (SQLException e) {
System.out.println("INSERTエラー:" + e.getMessage());
}
return 0;
}