底下代碼,可以將 Java Python 化,更方便、更直覺的操控 MySQL。請新增 mysql 目錄,將所有關於 mysql 的操作都被在 mysql 目錄中。
mysql.java
mysql.java 完整代碼如下
package net.ddns.mahaljsp.mysql;
import java.sql.*;
public final class mysql {
public static PyConnection connect(
String host,String user, String password, String database
)throws SQLException{
/*
//Java web 才需加入
try{
Class.forName("com.mysql.cj.jdbc.Driver");
}
catch (ClassNotFoundException ex){
System.out.println(ex);
}
*/
return new PyConnection(host, user, password, database);
}
}
Column.java
Column.java 為欄位自訂類別。
package net.ddns.mahaljsp.mysql;
public final class Column {
public String name;
public int type;
public Column(String name, int type){
this.name=name;
this.type=type;
}
}
PyConnection.java
PyConnection 為連線類別。
package net.ddns.mahaljsp.mysql;
import java.sql.*;
public final class PyConnection implements AutoCloseable{
Connection conn;
public PyConnection(
String host, String user, String password, String database
)throws SQLException{
//characterEncoding=UTF-8 最好是寫上,因為有的 driver 預設不是 utf-8
String connStr="jdbc:mysql://%s/%s?useSSL=true&serverTimezone=Asia/Taipei&characterEncoding=UTF-8"
.formatted(host, database);
conn=DriverManager.getConnection(connStr,user,password);
conn.setAutoCommit(false);
}
public Cursor cursor()throws SQLException{
return new Cursor(conn);
}
@Override
public void close()throws SQLException{
if (conn != null && !conn.isClosed()) {
conn.close();
conn = null;
}
}
public void commit()throws SQLException{
conn.commit();
}
public void rollback() throws SQLException {
conn.rollback();
}
}
Cursor.java
Cursor.java 為命令類別。
package net.ddns.mahaljsp.mysql;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public final class Cursor implements AutoCloseable {
private Connection conn;
private ResultSet rs;
PreparedStatement pstmt;
public Cursor(Connection conn){
this.conn=conn;
}
public void execute(String cmd)throws SQLException{
close(); // 關掉舊 rs
pstmt = conn.prepareStatement(cmd);
if (pstmt.execute()) {
rs = pstmt.getResultSet();
} else {
rs = null;
}
}
public void executemany(String cmd, List<List<Object>>datas)throws SQLException{
try(var ps = conn.prepareStatement(cmd)){
int count = ps.getParameterMetaData().getParameterCount();
for(var row:datas){
if (row.size()!=count)throw new SQLException("Parameter count mismatch");
for(int i=0;i<count;i++){
ps.setObject(i+1, row.get(i));
}
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
}
}
public List<List<Object>> fetchall()throws SQLException{
if (rs == null) {
throw new SQLException("No ResultSet to fetch");
}
try{
var meta=rs.getMetaData();
int count=meta.getColumnCount();
List<List<Object>> datas=new ArrayList<>();
while(rs.next()){
List<Object> item=new ArrayList<>();
for (int i=1;i<=count;i++){
item.add(rs.getObject(i));
}
datas.add(item);
}
return datas;
} finally {
rs.close();
rs = null;
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
}
}
public List<String> getColumns(String table)throws SQLException{
try (PreparedStatement ps =
conn.prepareStatement("select * from %s limit 0, 1".formatted(table));
ResultSet r = ps.executeQuery()) {
//pstmt=conn.prepareStatement("select * from %s limit 0, 1".formatted(table));
ResultSetMetaData meta=r.getMetaData();
List<String> data=new ArrayList<>();
for(int i=1;i<=meta.getColumnCount();i++){
data.add(meta.getColumnLabel(i));
}
return data;
}
}
public List<Column> getColumnsDetail(String table)throws SQLException{
try (PreparedStatement ps =
conn.prepareStatement("select * from %s limit 0, 1".formatted(table));
ResultSet r = ps.executeQuery()) {
ResultSetMetaData meta=r.getMetaData();
List<Column> data=new ArrayList<>();
for(int i=1;i<=meta.getColumnCount();i++){
data.add(new Column(meta.getColumnLabel(i), meta.getColumnType(i)));
}
return data;
}
}
public String getDDL(String table)throws SQLException{
if (!table.matches("[A-Za-z0-9_\\u4e00-\\u9fa5]+")) {
throw new SQLException("Invalid table name");
}
this.execute("show create table `%s`".formatted(table));
var rows = fetchall();
if (rows.isEmpty() || rows.get(0).size() < 2) {
throw new SQLException("Failed to get DDL for table " + table);
}
String ddl=rows.get(0).get(1).toString();
ddl = ddl.replaceAll(
//i : ignore case,大小寫不分(engine、ENGINE 都吃)
//s : dotall,讓 . 可以匹配 換行字元
//.* : 後面所有的字
"(?is)ENGINE=.*",
"ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
return ddl;
}
@Override
public void close()throws SQLException{
if (rs != null) {
rs.close();
rs = null;
}
if(pstmt !=null){
pstmt.close();
pstmt=null;
}
}
}
Select 應用
Select 的應用。
package filepath;
import net.ddns.mahaljsp.mysql.mysql;
import net.ddns.mahaljsp.os;
import java.sql.*;
public class Select {
public static void main(String[] args)throws SQLException {
String table="台灣股市";
os.utf8();
try(var conn=mysql.connect("mahaljsp.ddns.net", G.user,G.password, "cloud");
var cursor=conn.cursor();){
cursor.execute("select * from %s".formatted(table));
var rs=cursor.fetchall();
for(var r:rs){
System.out.println(r);
}
}
}
}
欄位取得
底下可以取得任何資料表的欄位 List。
package filepath;
import java.sql.*;
import net.ddns.mahaljsp.mysql.mysql;
import net.ddns.mahaljsp.os;
public class MysqlTest {
public static void main(String[] args) {
os.utf8();
try (var conn=mysql.connect(G.host, G.user, G.password, "django");
var cursor=conn.cursor();){
String cmd="select * from history";
cursor.execute(cmd);
var columns=cursor.getColumns("history");
System.out.println(columns);
} catch (SQLException ex) {
System.out.println(ex);
//ex.printStackTrace();
}
}
}
結果:
[id, ip, eventDay, eventTime, page, userAccount, country, city, lng, lat]
Copy 資料表
底下可以讀取資料表 DDL 及內容,再於另一台建立資料表及 copy 資料表內容。
package filepath;
import net.ddns.mahaljsp.mysql.mysql;
import java.sql.*;
import java.util.List;
import net.ddns.mahaljsp.os;
public class CopyTable {
public static void main(String[] args) {
os.utf8();
String table="台灣股市";
int count=0;
String ddl=null;
String columns=null;
List<List<Object>> rs=null;
try(var conn=mysql.connect(G.host, G.user, G.password, "cloud");
var cursor=conn.cursor();){
//取得欄位,不包含 id
List<String> tmp=cursor.getColumns(table);
tmp.remove(0);
count=tmp.size();
String s=tmp.toString();
columns=s.substring(1, s.length()-1);
ddl=cursor.getDDL(table);
//取得原始資料
String cmd="select %s from %s".formatted(columns, table);
cursor.execute(cmd);
rs=cursor.fetchall();
}
catch(SQLException e){}
if(rs!=null){
//寫入新的資料表
try(var conn=mysql.connect("localhost", G.user, G.password, "cloud");
var cursor=conn.cursor();){
cursor.execute("drop table if exists %s".formatted(table));
cursor.execute(ddl);
//cursor.execute("truncate table %s".formatted(table));
var cmd="insert into %s (%s) values (%s)"
.formatted(
table,
columns,
//"?,".repeat(count).substring(0,count*2-1)
//底下把字串集合(List、Set 等)用分隔符連起來。
String.join(",", Collections.nCopies(count, "?"))
);
cursor.executemany(cmd, rs);
conn.commit();
}
catch(SQLException e){
System.out.println(e);
}
}
}
}
DDL
底下可以讀取任一資料表的 DDL。
package filepath;
import net.ddns.mahaljsp.mysql.mysql;
import java.sql.*;
import net.ddns.mahaljsp.os;
public class DDL {
public static void main(String[] args) throws SQLException{
String table="台灣股市";
os.utf8();
try(var conn=mysql.connect("mahaljsp.ddns.net", G.user,G.password, "cloud");
var cursor=conn.cursor();){
String ddl=cursor.getDDL(table);
System.out.println(ddl);
conn.close();
/*
conn=mysql.connect("localhost", "thomas","Mahal$0507", "cloud");
cursor=conn.cursor();
cursor.execute("drop table if exists %s".formatted(table));
cursor.execute(ddl);
conn.close();
*/
}
}
}
結果 :
CREATE TABLE `台灣股市` (
`id` int NOT NULL AUTO_INCREMENT,
`代號` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
`日期` date NOT NULL,
`開盤` double NOT NULL,
`最高` double NOT NULL,
`最低` double NOT NULL,
`收盤` double NOT NULL,
`成交量` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
舊版代碼
請先新增 mysql.java,輸入如下完整代碼
package net.ddns.mahaljsp;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.ArrayList;
import java.sql.*;
import java.util.List;
public final class mysql {
public static PyConnection connect(
String host,String user, String password, String database
)throws SQLException{
/*
//Java web 才需加入
try{
Class.forName("com.mysql.cj.jdbc.Driver");
}
catch (ClassNotFoundException ex){
System.out.println(ex);
}
*/
return new PyConnection(host, user, password, database);
}
public final static class PyConnection{
Connection conn;
private PyConnection(
String host, String user, String password, String database
)throws SQLException{
String connStr="jdbc:mysql://%s/%s?useSSL=true&serverTimezone=Asia/Taipei"
.formatted(host, database);
conn=DriverManager.getConnection(connStr,user,password);
conn.setAutoCommit(false);
}
public Cursor cursor()throws SQLException{
return new Cursor(conn);
}
public void close()throws SQLException{
conn.close();
}
public void commit()throws SQLException{
conn.commit();
}
public void rollback() throws SQLException {
conn.rollback();
}
}
public final static class Cursor{
Connection conn;
private ResultSet rs;
private PreparedStatement ps;
private Cursor(Connection conn){
this.conn=conn;
}
public void execute(String cmd)throws SQLException{
rs=null;
ps = conn.prepareStatement(cmd);
if (ps.execute())rs=ps.getResultSet();
}
public void close()throws SQLException{
if (rs!=null)rs.close();
}
public void executemany(String cmd, List<List<Object>>datas)throws SQLException{
ps = conn.prepareStatement(cmd);
int count = ps.getParameterMetaData().getParameterCount();
for(var row:datas){
if (row.size()!=count)throw new SQLException("Parameter count mismatch");
for(int i=0;i<count;i++){
ps.setObject(i+1, row.get(i));
}
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
}
public Map<String, Integer> columns()throws SQLException{
ResultSetMetaData meta=rs.getMetaData();
Map<String, Integer> datas=new LinkedHashMap<>();
for(int i=1;i<=meta.getColumnCount();i++){
datas.put(meta.getColumnLabel(i), meta.getColumnType(i));
}
return datas;
}
public List<List<Object>> fetchall()throws SQLException{
var meta=rs.getMetaData();
int count=meta.getColumnCount();
List<List<Object>> datas=new ArrayList<>();
while(rs.next()){
List<Object> item=new ArrayList<>();
for (int i=1;i<=count;i++){
item.add(rs.getObject(i));
}
datas.add(item);
}
rs=null;
return datas;
}
}
}
查詢應用
主程式引用上面的 mysql,即可進行查詢
import java.sql.*;
import net.ddns.mahaljsp.mysql;
import net.ddns.mahaljsp.os;
public class MysqlTest {
public static void main(String[] args) {
os.utf8();
var host="host";
var user="帳號";
var password="密碼";
var database="資料庫";
try {
var conn=mysql.connect(host, user, password, database);
String cmd="select * from history";
var cursor=conn.cursor();
cursor.execute(cmd);
var columns=cursor.columns();
var rs=cursor.fetchall();
conn.close();
for(var r :rs){
System.out.println(r);
}
for (var c : columns.keySet()){
String type;
switch(columns.get(c)){
case Types.INTEGER->type="Integer";
case Types.VARCHAR->type="String";
case Types.TIMESTAMP->type="Timestamp";
case Types.DATE->type="Date";
case Types.TIME->type="time";
case Types.DOUBLE->type="Double";
default->type="Other";
}
System.out.printf("%s : %d, %s\n",c,columns.get(c), type);
}
} catch (SQLException ex) {
System.out.println(ex);
}
}
}
新增應用
主程式引用上面的 mysql,即可快速新增多筆資料。
public static void main(String[] args) {
try{
var conn=mysql.connect("mahaljsp.ddns.net", "帳號", "密碼", "cloud");
var cursor=conn.cursor();
cursor.execute("select 日期, 買進, 賣出 from 台銀黃金");
var rs=cursor.fetchall();
for(var r:rs){
System.out.println(r);
}
conn.close();
conn=mysql.connect("localhost", "帳號", "密碼", "cloud");
cursor=conn.cursor();
cursor.execute("truncate table 台銀黃金");
String cmd="insert into 台銀黃金 (日期, 買進, 賣出) values (?,?,?)";
cursor.executemany(cmd, rs);
conn.commit();
conn.close();
}
catch(SQLException e){}
}
