这是 MySQL 数据库的 Java 教程。它涵盖了使用 JDBC 使用 Java 进行 MySQL 编程的基础知识。
在本教程中,我们使用?MySQL Connector/J驱动程序。它是 MySQL 的官方 JDBC 驱动程序。这些示例是在 Ubuntu Linux 上创建和测试的。
JDBC
JDBC是一种用于 Java 编程语言的 API,它定义了客户端如何访问数据库。它提供了查询和更新数据库中数据的方法。JDBC面向关系数据库。从技术角度来看,API 是 java.sql 包中的一组类。要将 JDBC 用于特定数据库,我们需要该数据库的 JDBC 驱动程序。
JDBC 是 Java 数据库编程的基石。今天,它被认为是非常低级的并且容易出错。创建诸如 MyBatis 或 JdbcTemplate 之类的解决方案是为了减轻 JDBC 编程的负担。然而,在底层,这些解决方案仍然使用 JDBC。JDBC 是 Java 标准版平台的一部分。
JDBC 管理这三个主要的编程活动:
- 连接到数据库;
- 向数据库发送查询和更新语句;
- 检索和处理从数据库接收到的结果以响应查询。
MySQL 连接器/J
为了在 Java 中连接 MySQL,MySQL 提供了 MySQL Connector/J,这是一个实现 JDBC API 的驱动程序。MySQL Connector/J 是 JDBC Type 4 驱动程序。类型 4 表示驱动程序是 MySQL 协议的纯 Java 实现,不依赖于 MySQL 客户端库。在本教程中,我们使用 MySQL Connector/J 5.1.41,它是 5.1 生产分支的维护版本。
连接字符串
数据库连接是用连接字符串定义的。它包含诸如数据库类型、数据库名称、服务器名称和端口号等信息。它还可能包含用于配置的附加键/值对。每个数据库都有自己的连接字符串格式。
以下是 MySQL 连接字符串的语法:
jdbc:mysql://[host1][:port1][,[host2][:port2]]...[/[database]]
[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
可以为服务器故障转移设置指定多个主机。方括号中的项目是可选的。如果未指定主机,则主机名默认为 localhost。如果未指定主机的端口,则默认为 3306,这是 MySQL 服务器的默认端口号。
jdbc:mysql://localhost:3306/testdb?useSSL=false
这是 MySQL 连接字符串的示例。被jdbc:mysql:// 称为子协议并且对于 MySQL 来说是常量。我们连接到?localhost MySQL 标准端口 3306。数据库名称是testdb .?附加的键/值对跟在问号字符 (?) 之后。useSSL=false 告诉 MySQL 不会有安全连接?。
关于 MySQL 数据库
MySQL 是领先的开源数据库管理系统。它是一个多用户、多线程的数据库管理系统。MySQL 在网络上特别流行。它是由 Linux、Apache、MySQL 和 PHP 组成的非常流行的LAMP平台的一部分。目前 MySQL 归甲骨文所有。MySQL 数据库可在大多数重要的操作系统平台上使用。它在 BSD Unix、Linux、Windows 或 Mac OS 上运行。维基百科和 YouTube 使用 MySQL。这些网站每天管理数百万个查询。MySQL有两个版本:MySQL服务器系统和MySQL嵌入式系统。
设置 MySQL
在本节中,我们将安装 MySQL 服务器、创建testdb ?数据库和测试用户。
$ sudo apt-get install mysql-server
此命令安装 MySQL 服务器和各种其他包。安装软件包时,系统会提示我们输入 MySQL root 帐户的密码。
接下来,我们将创建一个新的数据库用户和一个新的数据库。我们使用mysql 客户端。
$ sudo service mysql status
mysql start/running, process 5129
我们检查 MySQL 服务器是否正在运行。如果没有,我们需要启动服务器。在 Ubuntu Linux 上,这可以通过sudo service mysql start 命令完成。
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 43
Server version: 5.7.21-0ubuntu0.16.04.1 (Ubuntu)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
+--------------------+
2 rows in set (0.00 sec)
我们使用mysql 监控客户端应用程序连接到服务器。我们使用 root 帐户连接到数据库。SHOW DATABASES 我们使用该语句?显示所有可用的数据库。
mysql> CREATE DATABASE testdb;
Query OK, 1 row affected (0.02 sec)
我们创建一个新的testdb 数据库。我们将在整个教程中使用这个数据库。
mysql> CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'test623';
Query OK, 0 rows affected (0.00 sec)
mysql> USE testdb;
Database changed
mysql> GRANT ALL ON testdb.* TO 'testuser'@'localhost';
Query OK, 0 rows affected (0.00 sec)
mysql> quit;
Bye
我们创建一个新的数据库用户。我们授予该用户对 testdb 数据库的所有表的所有权限。
Maven文件
我们使用以下 Maven 文件:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zetcode</groupId>
<artifactId>AppName</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>com.zetcode.AppName</mainClass>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
</configuration>
</plugin>
</plugins>
</build>
<name>AppName</name>
</project>
POM 文件依赖于 MySQL 驱动程序。我们还包括exec-maven-plugin ?用于从 Maven 执行 Java 程序。在<mainClass></mainClass> 标签之间,我们提供了应用程序的全名。
Java MySQL 版本
如果以下程序运行正常,那么我们已经安装好了一切。我们检查 MySQL 服务器的版本。
JdbcMySQLVersion.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcMySQLVersion {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String query = "SELECT VERSION()";
try (Connection con = DriverManager.getConnection(url, user, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query)) {
if (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcMySQLVersion.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们连接到数据库并获取有关 MySQL 服务器的一些信息。
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
这是 MySQL 数据库的连接 URL。每个驱动程序都有不同的 URL 语法。在我们的例子中,我们提供了一个主机、一个端口和一个数据库名称。
try (Connection con = DriverManager.getConnection(url, user, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query)) {
我们使用连接 URL、用户名和密码建立到数据库的连接。使用该?getConnection() 方法建立连接。
连接对象的createStatement() 方法创建一个Statement 用于向数据库发送 SQL 语句的对象。
连接对象的executeQuery() 方法执行给定的 SQL 语句,返回单个ResultSet 对象。这ResultSet 是由特定 SQL 语句返回的数据表。
该try-with-resources 语法可确保最终清理资源。
if (result.next()) {
System.out.println(result.getString(1));
}
一个ResultSet 对象维护一个指向其当前数据行的游标。最初,光标位于第一行之前。该next() ?方法将光标移动到下一行。如果没有剩余行,则该方法返回false 。该getString() 方法检索指定列的值。第一列的索引为 1。
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcMySQLVersion.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
如果出现异常,我们会记录错误消息。对于此控制台示例,消息显示在终端中。
$ mvn exec:java -q
5.7.21-0ubuntu0.16.04.1
我们从命令行运行程序。Manen 的-q 选项以安静模式运行 Maven;即我们只看到错误消息。
创建和填充表
接下来我们将创建数据库表并用数据填充它们。这些表将在本教程中使用。
mysql_tables.sql
USE testdb;
DROP TABLE IF EXISTS Books, Authors, Testing, Images;
CREATE TABLE Authors(Id BIGINT PRIMARY KEY AUTO_INCREMENT, Name VARCHAR(100));
CREATE TABLE Books(Id BIGINT PRIMARY KEY AUTO_INCREMENT, AuthorId BIGINT,
Title VARCHAR(100), FOREIGN KEY(AuthorId) REFERENCES Authors(Id)
ON DELETE CASCADE);
CREATE TABLE Testing(Id INT);
CREATE TABLE Images(Id INT PRIMARY KEY AUTO_INCREMENT, Data MEDIUMBLOB);
INSERT INTO Authors(Id, Name) VALUES(1, 'Jack London');
INSERT INTO Authors(Id, Name) VALUES(2, 'Honore de Balzac');
INSERT INTO Authors(Id, Name) VALUES(3, 'Lion Feuchtwanger');
INSERT INTO Authors(Id, Name) VALUES(4, 'Emile Zola');
INSERT INTO Authors(Id, Name) VALUES(5, 'Truman Capote');
INSERT INTO Books(Id, AuthorId, Title) VALUES(1, 1, 'Call of the Wild');
INSERT INTO Books(Id, AuthorId, Title) VALUES(2, 1, 'Martin Eden');
INSERT INTO Books(Id, AuthorId, Title) VALUES(3, 2, 'Old Goriot');
INSERT INTO Books(Id, AuthorId, Title) VALUES(4, 2, 'Cousin Bette');
INSERT INTO Books(Id, AuthorId, Title) VALUES(5, 3, 'Jew Suess');
INSERT INTO Books(Id, AuthorId, Title) VALUES(6, 4, 'Nana');
INSERT INTO Books(Id, AuthorId, Title) VALUES(7, 4, 'The Belly of Paris');
INSERT INTO Books(Id, AuthorId, Title) VALUES(8, 5, 'In Cold blood');
INSERT INTO Books(Id, AuthorId, Title) VALUES(9, 5, 'Breakfast at Tiffany');
SQL 命令创建四个数据库表:Authors 、?Books 、Testing 和Images 。这些表是 InnoDB 类型的。InnoDB 数据库支持外键约束和事务。AuthorId ?我们在表的列上放置一个外键约束Books 。?我们用初始数据?填充Authors 和表。Books
mysql> source mysql_tables.sql
Query OK, 0 rows affected (0.07 sec)
Query OK, 0 rows affected (0.12 sec)
Query OK, 1 row affected (0.04 sec)
...
我们使用source 命令来执行tables.sql 脚本。
Java MySQL 准备好的语句
现在我们将关注准备好的陈述。当我们编写准备好的语句时,我们使用占位符而不是直接将值写入语句。准备好的语句提高了安全性和性能。
在 Java 中,aPreparedStatement 是一个表示预编译 SQL 语句的对象。
JdbcPrepared.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcPrepared {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String author = "Trygve Gulbranssen";
String sql = "INSERT INTO Authors(Name) VALUES(?)";
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, author);
pst.executeUpdate();
System.out.println("A new author has been inserted");
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcPrepared.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们将新作者添加到Authors 表中。
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
在这里,我们创建一个准备好的语句。当我们编写准备好的语句时,我们使用占位符而不是直接将值写入语句。准备好的语句速度更快,可以防止 SQL 注入攻击。这? 是一个占位符,稍后将被填充。
pst.setString(1, author);
值绑定到占位符。
pst.executeUpdate();
准备好的语句被执行。executeUpdate() ?当我们不期望返回任何数据时,我们使用语句对象的方法。这是我们创建数据库或执行INSERT ,?UPDATE ,?DELETE 语句的时候。
$ mvn exec:java -q
A new author has been inserted
mysql> select * from Authors;
+----+--------------------+
| Id | Name |
+----+--------------------+
| 1 | Jack London |
| 2 | Honore de Balzac |
| 3 | Lion Feuchtwanger |
| 4 | Emile Zola |
| 5 | Truman Capote |
| 6 | Trygve Gulbranssen |
+----+--------------------+
6 rows in set (0.00 sec)
我们在表中插入了一个新作者。
测试 MySQL 准备好的和未准备好的语句
对于以下两个示例,我们将使用测试表。我们将执行一个普通语句和一个准备好的语句 5000 次。我们检查执行时间是否存在差异。
JdbcNotPreparedTesting.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcNotPreparedTesting {
public static void main(String[] args) {
String cs = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
try (Connection con = DriverManager.getConnection(cs, user, password);
Statement st = con.createStatement()) {
for (int i = 1; i <= 5000; i++) {
String sql = "INSERT INTO Testing(Id) VALUES(" + 2 * i + ")";
st.executeUpdate(sql);
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcNotPreparedTesting.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
第一个示例使用普通Statement 对象。
for (int i = 1; i <= 5000; i++) {
String sql = "INSERT INTO Testing(Id) VALUES(" + 2 * i + ")";
st.executeUpdate(sql);
}
我们构建查询并执行 5000 次。
$ time mvn exec:java -q
real 4m14.716s
user 0m6.820s
sys 0m0.404s
完成 5000 个插入需要 4.14 分钟。
JdbcPreparedTesting.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcPreparedTesting {
public static void main(String[] args) {
String cs = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String sql = "INSERT INTO Testing(Id) VALUES(?)";
try (Connection con = DriverManager.getConnection(cs, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
for (int i = 1; i <= 5000; i++) {
pst.setInt(1, i * 2);
pst.executeUpdate();
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcPreparedTesting.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
现在我们使用PreparedStatement 来完成同样的任务。
try (Connection con = DriverManager.getConnection(cs, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
prepareStatement() ?我们使用该方法?创建准备好的语句。
for (int i = 1; i <= 5000; i++) {
pst.setInt(1, i * 2);
pst.executeUpdate();
}
我们将一个值绑定到准备好的语句,循环执行一千次。
$ time mvn exec:java -q
real 3m53.962s
user 0m6.280s
sys 0m0.380s
现在完成 5000 个插入需要 3.53 分钟。我们节省了 20 秒。
Java MySQL 检索数据
接下来我们将展示如何从数据库表中检索数据。我们从Authors 表中获取所有数据。
JdbcRetrieve.java
package com.zetcode;
import java.sql.PreparedStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcRetrieve {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String query = "SELECT * FROM Authors";
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcRetrieve.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们从Authors 表中获取所有作者并将它们打印到控制台。
String query = "SELECT * FROM Authors";
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
我们执行一个从表中选择所有列的查询Authors 。我们使用executeQuery() 方法。该方法执行给定的 SQL 语句,该语句返回单个ResultSet 对象。ResultSet 是 SQL 查询返回的数据表?。
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
该next() 方法将光标前进到下一条记录。它false 在结果集中没有更多行时返回。getInt() andgetString() 方法检索此?ResultSet 对象当前行中指定列的值,作为Java编程语言的int and?String ?。
$ mvn exec:java -q
1: Jack London
2: Honore de Balzac
3: Lion Feuchtwanger
4: Emile Zola
5: Truman Capote
6: Trygve Gulbranssen
我们执行程序;我们将作者的 ID 和姓名打印到控制台。
特性
将程序外部的配置数据放在单独的文件中是一种常见的做法。这样程序员就更灵活了。我们可以更改用户、密码或连接 url,而无需重新编译程序。它在需要大量测试、调试、保护数据等的动态环境中特别有用。
在 Java 中,Properties 是一个经常用于此目的的类。该类用于轻松读取和保存键/值属性。
db.properties
db.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
db.user=testuser
db.passwd=test623
我们有一个db.properties 文件,其中包含三个键/值对。这些是在程序执行期间动态加载的。
JdbcProperties.java
package com.zetcode;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcProperties {
private static Properties getConnectionData() {
Properties props = new Properties();
String fileName = "src/main/resources/db.properties";
try (FileInputStream in = new FileInputStream(fileName)) {
props.load(in);
} catch (IOException ex) {
Logger lgr = Logger.getLogger(JdbcProperties.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
return props;
}
public static void main(String[] args) {
Properties props = getConnectionData();
String url = props.getProperty("db.url");
String user = props.getProperty("db.user");
String passwd = props.getProperty("db.passwd");
String query = "SELECT * FROM Authors";
try (Connection con = DriverManager.getConnection(url, user, passwd);
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcProperties.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们连接到 testdb 数据库并将?Authors 表的内容打印到控制台。这一次,我们从文件中加载连接属性。它们没有硬编码在程序中。
Properties props = new Properties();
String fileName = "src/main/resources/db.properties";
try (FileInputStream in = new FileInputStream(fileName)) {
props.load(in);
} catch (IOException ex) {
Logger lgr = Logger.getLogger(JdbcProperties.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
类Properties 已创建。数据是从名为 的文件中加载的db.properties ,其中我们有我们的配置数据。
String url = props.getProperty("db.url");
String user = props.getProperty("db.user");
String passwd = props.getProperty("db.passwd");
使用该getProperty() ?方法检索值。
Java MySQL 数据源
在此示例中,我们使用数据源连接到数据库。数据源的使用提高了应用程序的性能和可伸缩性。使用数据源有几个优点DriverManager :增加可移植性、连接池和分布式事务。
这MysqlDataSource 是一个用于创建数据源的类。
db.properties
# mysql properties
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
mysql.username=testuser
mysql.password=test623
这是 MySQL 数据库的属性。
ComLineDSEx.java
package com.zetcode;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ComLineDSEx {
public static MysqlDataSource getMySQLDataSource() {
Properties props = new Properties();
String fileName = "src/main/resources/db.properties";
try (FileInputStream fis = new FileInputStream(fileName)) {
props.load(fis);
} catch (IOException ex) {
Logger lgr = Logger.getLogger(ComLineDSEx.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
MysqlDataSource ds = new MysqlDataSource();
ds.setURL(props.getProperty("mysql.url"));
ds.setUser(props.getProperty("mysql.username"));
ds.setPassword(props.getProperty("mysql.password"));
return ds;
}
public static void main(String[] args) {
MysqlDataSource ds = getMySQLDataSource();
String query = "SELECT VERSION()";
try (Connection con = ds.getConnection();
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
if (rs.next()) {
String version = rs.getString(1);
System.out.println(version);
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(ComLineDSEx.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
在此示例中,我们使用数据源连接到数据库。
String fileName = "src/main/resources/db.properties";
try (FileInputStream fis = new FileInputStream(fileName)) {
props.load(fis);
} catch (IOException ex) {
Logger lgr = Logger.getLogger(ComLineDSEx.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
db.properties 从文件?中读取数据库属性。
MysqlDataSource ds = new MysqlDataSource();
ds.setURL(props.getProperty("mysql.url"));
ds.setUser(props.getProperty("mysql.username"));
ds.setPassword(props.getProperty("mysql.password"));
创建了AMysqlDataSource 并设置了数据源属性。
try (Connection con = ds.getConnection();
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
从数据源创建一个连接对象。
Java MySQL 多语句
可以在一个查询中执行多个 SQL 语句。allowMultiQueries 必须设置为在 MySQL 中启用多个语句?。
JdbcMulStat.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcMulStat {
public static void main(String[] args) throws SQLException {
String cs = "jdbc:mysql://localhost:3306/"
+ "testdb?allowMultiQueries=true&useSSL=false";
String user = "testuser";
String password = "test623";
String query = "SELECT Id, Name FROM Authors WHERE Id=1;"
+ "SELECT Id, Name FROM Authors WHERE Id=2;"
+ "SELECT Id, Name FROM Authors WHERE Id=3";
try (Connection con = DriverManager.getConnection(cs, user, password);
PreparedStatement pst = con.prepareStatement(query);) {
boolean isResult = pst.execute();
do {
try (ResultSet rs = pst.getResultSet()) {
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
isResult = pst.getMoreResults();
}
} while (isResult);
}
}
}
Authors 在代码示例中,我们从表中?检索三行。我们使用三个SELECT 语句来获取三行。
String cs = "jdbc:mysql://localhost:3306/"
+ "testdb?allowMultiQueries=true&useSSL=false";
allowMultiQueries 我们通过将参数设置为 true 来?启用数据库 URL 中的多个语句查询。
String query = "SELECT Id, Name FROM Authors WHERE Id=1;"
+ "SELECT Id, Name FROM Authors WHERE Id=2;"
+ "SELECT Id, Name FROM Authors WHERE Id=3";
在这里,我们有一个包含多个语句的查询。语句用分号隔开。
boolean isResult = pst.execute();
我们调用execute() prepared statement对象的方法。该方法返回一个布尔值,指示第一个结果是否为ResultSet 对象。getMoreResults() 使用该方法?调用后续结果。
do {
try (ResultSet rs = pst.getResultSet()) {
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
isResult = pst.getMoreResults();
}
} while (isResult);
结果的处理在do while 循环内完成。使用?方法调用ResultSet 检索。getResultSet() 为了找出是否有其他结果,我们调用该?getMoreResults() 方法。
$ mvn exec:java -q
1: Jack London
2: Honore de Balzac
3: Lion Feuchtwanger
这是示例的输出。Authors 从表?中检索前三行 。
Java MySQL 列标题
以下示例显示如何使用数据库表中的数据打印列标题。我们将列名称为元数据。元数据是关于数据库中核心数据的数据。
JdbcColumnHeaders.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcColumnHeaders {
public static void main(String[] args) {
String cs = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String query = "SELECT Name, Title From Authors, "
+ "Books WHERE Authors.Id=Books.AuthorId";
try (Connection con = DriverManager.getConnection(cs, user, password);
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
ResultSetMetaData meta = rs.getMetaData();
String colname1 = meta.getColumnName(1);
String colname2 = meta.getColumnName(2);
String header = String.format("%-21s%s", colname1, colname2);
System.out.println(header);
while (rs.next()) {
String row = String.format("%-21s", rs.getString(1));
System.out.print(row);
System.out.println(rs.getString(2));
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcColumnHeaders.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
在这个程序中,我们从表中选择作者,从Authors 表中选择他们的书Books 。我们打印结果集中返回的列的名称。输出被格式化。
String query = "SELECT Name, Title From Authors, " +
"Books WHERE Authors.Id=Books.AuthorId";
这是连接作者和他们的书的 SQL 语句。
ResultSetMetaData meta = rs.getMetaData();
要获取列名,我们需要获取ResultSetMetaData .?它是一个对象,可用于获取有关ResultSet 对象中列的类型和属性的信息。
String colname1 = meta.getColumnName(1);
String colname2 = meta.getColumnName(2);
从获得的元数据中,我们得到列名。
String header = String.format("%-21s%s", colname1, colname2);
System.out.println(header);
我们将列名打印到控制台。
while (rs.next()) {
String row = String.format("%-21s", rs.getString(1));
System.out.print(row);
System.out.println(rs.getString(2));
}
我们将数据打印到控制台。第一列是 21 个字符宽,并与左侧对齐。
$ mvn exec:java -q
NAME Title
Jack London Call of the Wild
Jack London Martin Eden
Honore de Balzac Old Goriot
Honore de Balzac Cousin Bette
Lion Feuchtwanger Jew Suess
Emile Zola Nana
Emile Zola The Belly of Paris
Truman Capote In Cold blood
Truman Capote Breakfast at Tiffany
这是程序的输出。
MySQL Java 自动生成的密钥
MySQL 的AUTO_INCREMENT 属性为新行生成一个唯一 ID。以下示例显示了我们如何使用 JDBC 检索自动生成的键值。
JdbcAutoGenKey.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcAutoGenKey {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String author = "Oscar Wilde";
String sql = "INSERT INTO Authors(Name) VALUES(?)";
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
pst.setString(1, author);
pst.executeUpdate();
try (ResultSet rs = pst.getGeneratedKeys()) {
if (rs.first()) {
System.out.printf("The ID of new author: %d", rs.getLong(1));
}
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcAutoGenKey.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
在示例中,我们将新作者添加到其主键由 MySQL 自动递增的表中。我们检索生成的 ID。
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
作为第一步,我们必须将 传递Statement.RETURN_GENERATED_KEYS ?给该prepareStatement() 方法。
try (ResultSet rs = pst.getGeneratedKeys()) {
getGeneratedKeys() 然后我们使用该方法?检索生成的密钥。
if (rs.first()) {
System.out.printf("The ID of new author: %d", rs.getLong(1));
}
由于我们只有一个插入语句,我们使用first() ?导航到该值。
$ mvn exec:java -q
The ID of new author: 7
这是一个示例输出。
MySQL Java 写入图像
有些人喜欢将他们的图像放入数据库中,有些人喜欢将它们保存在文件系统中以供他们的应用程序使用。当我们处理大量图像时,就会出现技术难题。图像是二进制数据。MySQL 数据库有一种特殊的数据类型来存储二进制数据,称为BLOB (Binary Large Object)。
对于此示例,我们使用该Images 表。
JdbcWriteImage.java
package com.zetcode;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcWriteImage {
public static void main(String[] args) {
String cs = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String sql = "INSERT INTO Images(Data) VALUES(?)";
try (Connection con = DriverManager.getConnection(cs, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
File myFile = new File("src/main/resources/tree.png");
try (FileInputStream fin = new FileInputStream(myFile)) {
pst.setBinaryStream(1, fin, (int) myFile.length());
pst.executeUpdate();
} catch (IOException ex) {
Logger lgr = Logger.getLogger(JdbcWriteImage.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcWriteImage.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
在前面的示例中,我们从当前工作目录中读取了一个 PNG 图像并插入到Images 表中。
String sql = "INSERT INTO Images(Data) VALUES(?)";
这是插入图像的 SQL。
File myFile = new File("src/main/resources/tree.png");
try (FileInputStream fin = new FileInputStream(myFile)) {
我们为图像文件创建一个File 对象。为了从这个文件中读取字节,我们创建了一个FileInputStream ?对象。
pst.setBinaryStream(1, fin, (int) myFile.length());
二进制流设置为准备好的语句。该setBinaryStream() 方法的参数是要绑定的参数索引、输入流和流中的字节数。
pst.executeUpdate();
我们执行语句。
MySQL Java 读取图像
在前面的示例中,我们已将图像插入到数据库表中。现在我们将从表中读取图像。
JdbcReadImage.java
package com.zetcode;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcReadImage {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String query = "SELECT Data FROM Images LIMIT 1";
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(query);
ResultSet result = pst.executeQuery()) {
if (result.next()) {
String fileName = "src/main/resources/tree.png";
try (FileOutputStream fos = new FileOutputStream(fileName)) {
Blob blob = result.getBlob("Data");
int len = (int) blob.length();
byte[] buf = blob.getBytes(1, len);
fos.write(buf, 0, len);
} catch (IOException ex) {
Logger lgr = Logger.getLogger(JdbcReadImage.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcReadImage.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们从图像表中读取了一张图像。
String query = "SELECT Data FROM Images LIMIT 1";
我们从表中选择一条记录。
String fileName = "src/main/resources/tree.png";
try (FileOutputStream fos = new FileOutputStream(fileName)) {
创建对象以FileOutputStream 写入文件。它用于写入原始字节流,例如图像数据。
Blob blob = result.getBlob("Data");
Data 我们通过调用该getBlob() 方法?从列中获取图像数据。
int len = (int) blob.length();
我们计算出 blob 数据的长度。换句话说,我们得到了字节数。
byte[] buf = blob.getBytes(1, len);
该getBytes() 方法以字节数组的形式检索 Blob 对象的所有字节。
fos.write(buf, 0, len);
字节被写入输出流。图像是在文件系统上创建的。
交易支持
事务是针对 一个或多个数据库中的数据的数据库操作的原子单元。事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。
MySQL 数据库有不同类型的存储引擎。最常见的是 MyISAM 和 InnoDB 引擎。数据安全性和数据库速度之间存在权衡。MyISAM 表处理速度更快,并且它们不支持事务。另一方面,InnoDB 表对数据丢失更安全。它们支持事务并且处理速度较慢。
JdbcTransaction.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcTransaction {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
try (Connection con = DriverManager.getConnection(url, user, password)) {
try (Statement st = con.createStatement()) {
con.setAutoCommit(false);
st.executeUpdate("UPDATE Authors SET Name = 'Leo Tolstoy'"
+ "WHERE Id = 1");
st.executeUpdate("UPDATE Books SET Title = 'War and Peace'"
+ "WHERE Id = 1");
st.executeUpdate("UPDATE Books SET Titl = 'Anna Karenina'"
+ "WHERE Id = 2");
con.commit();
} catch (SQLException ex) {
try {
con.rollback();
} catch (SQLException ex1) {
Logger lgr = Logger.getLogger(JdbcTransaction.class.getName());
lgr.log(Level.WARNING, ex1.getMessage(), ex1);
}
Logger lgr = Logger.getLogger(JdbcTransaction.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
} catch (SQLException ex) {
Logger.getLogger(JdbcTransaction.class.getName()).log(
Level.SEVERE, null, ex);
}
}
}
在这个程序中,我们要更改Authors 表格第一行的作者姓名。我们还必须更改与该作者关联的书籍。这是需要交易的一个很好的例子。如果我们更改作者而不更改作者的书籍,则数据已损坏。
con.setAutoCommit(false);
要处理事务,我们必须将自动提交模式设置为 false。默认情况下,数据库连接处于自动提交模式。在这种模式下,每条语句在执行后立即提交给数据库。声明无法撤消。当自动提交关闭时,我们通过调用来提交更改?commit() 或通过调用方法回滚?rollback() 。
st.executeUpdate("UPDATE Books SET Titl = 'Anna Karenina' "
+ "WHERE Id = 2");
第三条 SQL 语句有错误。表中没有标题列。
con.commit();
如果没有异常,则提交事务。
try {
con.rollback();
} catch (SQLException ex1) {
Logger lgr = Logger.getLogger(JdbcTransaction.class.getName());
lgr.log(Level.WARNING, ex1.getMessage(), ex1);
}
如果发生异常,事务将回滚。不会对数据库提交任何更改。
Feb 21, 2018 2:35:14 PM com.zetcode.JdbcTransaction main
SEVERE: Unknown column 'Titl' in 'field list'
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
Unknown column 'Titl' in 'field list'
应用程序以异常结束。
mysql> SELECT Name, Title From Authors, Books WHERE Authors.Id=Books.AuthorId;
+-------------------+----------------------+
| Name | Title |
+-------------------+----------------------+
| Jack London | Call of the Wild |
| Jack London | Martin Eden |
| Honore de Balzac | Old Goriot |
| Honore de Balzac | Cousin Bette |
| Lion Feuchtwanger | Jew Suess |
| Emile Zola | Nana |
| Emile Zola | The Belly of Paris |
| Truman Capote | In Cold blood |
| Truman Capote | Breakfast at Tiffany |
+-------------------+----------------------+
9 rows in set (0.01 sec)
事务已回滚,没有发生任何更改。
但是,没有交易,数据是不安全的。
JdbcNoTransaction.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcNoTransaction {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
try (Connection con = DriverManager.getConnection(url, user, password);
Statement st = con.createStatement()) {
st.executeUpdate("UPDATE Authors SET Name = 'Leo Tolstoy'"
+ "WHERE Id = 1");
st.executeUpdate("UPDATE Books SET Title = 'War and Peace'"
+ "WHERE Id = 1");
st.executeUpdate("UPDATE Books SET Titl = 'Anna Karenina'"
+ "WHERE Id = 2");
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcNoTransaction.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们有同样的例子。这一次,没有事务支持。
mysql> SELECT Name, Title From Authors, Books WHERE Authors.Id=Books.AuthorId;
+-------------------+----------------------+
| Name | Title |
+-------------------+----------------------+
| Leo Tolstoy | War and Peace |
| Leo Tolstoy | Martin Eden |
| Honore de Balzac | Old Goriot |
| Honore de Balzac | Cousin Bette |
| Lion Feuchtwanger | Jew Suess |
| Emile Zola | Nana |
| Emile Zola | The Belly of Paris |
| Truman Capote | In Cold blood |
| Truman Capote | Breakfast at Tiffany |
+-------------------+----------------------+
9 rows in set (0.00 sec)
再次抛出异常。列夫·托尔斯泰(Leo Tolstoy)没有写马丁·伊登(Martin Eden);数据已损坏。
批量更新
当我们需要用多个语句更新数据时,我们可以使用批量更新。批量更新可用于INSERT ,?UPDATE ,?DELETE , 语句以及 for?CREATE TABLE 和DROP TABLE 语句。
JdbcBatchUpdate.java
package com.zetcode;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcBatchUpdate {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
try (Connection con = DriverManager.getConnection(url, user, password)) {
try (Statement st = con.createStatement()) {
con.setAutoCommit(false);
st.addBatch("DROP TABLE IF EXISTS Authors2");
st.addBatch("CREATE TABLE Authors2(Id INT PRIMARY KEY, "
+ "Name VARCHAR(100))");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(1, 'Jack London')");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(2, 'Honore de Balzac')");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(3, 'Lion Feuchtwanger')");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(4, 'Emile Zola')");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(5, 'Truman Capote')");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(6, 'Umberto Eco')");
int counts[] = st.executeBatch();
con.commit();
System.out.printf("Committed %d updates", counts.length);
} catch (SQLException ex) {
try {
con.rollback();
} catch (SQLException ex2) {
Logger lgr = Logger.getLogger(JdbcBatchUpdate.class.getName());
lgr.log(Level.FINEST, ex2.getMessage(), ex2);
}
Logger lgr = Logger.getLogger(JdbcBatchUpdate.class.getName());
lgr.log(Level.FINEST, ex.getMessage(), ex);
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcBatchUpdate.class.getName());
lgr.log(Level.FINEST, ex.getMessage(), ex);
}
}
}
这是一个批量更新的示例程序。我们从 Authors 表中删除所有数据并插入新数据。我们添加了一位新作者 Umberto Eco 以查看更改。
st.addBatch("DROP TABLE IF EXISTS Authors2");
st.addBatch("CREATE TABLE Authors2(Id INT PRIMARY KEY, "
+ "Name VARCHAR(100))");
st.addBatch("INSERT INTO Authors2(Id, Name) "
+ "VALUES(1, 'Jack London')");
...
我们使用 tehaddBatch() 方法将新命令添加到语句中。
int counts[] = st.executeBatch();
添加完所有命令后,我们调用executeBatch() 来执行批量更新。该方法返回一组已提交的更改。
con.commit();
批量更新在事务中提交。
} catch (SQLException ex) {
try {
con.rollback();
} catch (SQLException ex2) {
Logger lgr = Logger.getLogger(JdbcBatchUpdate.class.getName());
lgr.log(Level.FINEST, ex2.getMessage(), ex2);
}
Logger lgr = Logger.getLogger(JdbcBatchUpdate.class.getName());
lgr.log(Level.FINEST, ex.getMessage(), ex);
}
我们打电话rollback() 以防批量更新失败。
$ mvn exec:java -q
Committed 8 updates
mysql> SELECT * from Authors2;
+----+-------------------+
| Id | Name |
+----+-------------------+
| 1 | Jack London |
| 2 | Honore de Balzac |
| 3 | Lion Feuchtwanger |
| 4 | Emile Zola |
| 5 | Truman Capote |
| 6 | Umberto Eco |
+----+-------------------+
6 rows in set (0.00 sec)
我们执行BatchUpdate 程序。该SELECT 语句显示Authors2 表已成功更新。它有一位新作者,Umerto Eco。
将数据导出到 CSV 文件
下一个示例将数据导出到 CSV 文件中。
我们需要为我们的testuser ;拥有适当的文件权限。否则,我们会收到拒绝访问错误消息。
mysql> GRANT FILE ON *.* TO 'testuser'@'localhost';
我们设置了FILE 权限。
mysql> SHOW VARIABLES LIKE "secure_file_priv";
+------------------+-----------------------+
| Variable_name | Value |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
1 row in set (0.26 sec)
出于安全原因,MySQL 以--secure-file-priv 启用选项开始,它只允许使用特定目录中的文件。目录在secure_file_priv 变量中指定。在 Windows 上,路径类似于'C:/ProgramData/MySQL/MySQL Server 5.7/Uploads' .
ExportCSV.java
package com.zetcode;
import java.sql.PreparedStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdbcExportCSV {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
String user = "testuser";
String password = "test623";
String query = "SELECT Name, Title INTO OUTFILE "
+ "'/var/lib/mysql-files/authors_books.csv' "
+ "FIELDS TERMINATED BY ',' "
+ "FROM Authors, Books WHERE "
+ "Authors.Id=Books.AuthorId";
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(query)) {
pst.execute();
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcExportCSV.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
我们将作者及其对应的书籍导出到/var/lib/mysql-files/authors_books.csv ?文件中。
String query = "SELECT Name, Title INTO OUTFILE "
+ "'/var/lib/mysql-files/authors_books.csv' "
+ "FIELDS TERMINATED BY ',' "
+ "FROM Authors, Books WHERE "
+ "Authors.Id=Books.AuthorId";
要将数据导出到文件中,我们使用SELECT INTO OUTFILE SQL 语句。
$ cat /var/lib/mysql-files/authors_books.csv
Jack London,Call of the Wild
Jack London,Martin Eden
Honore de Balzac,Old Goriot
Honore de Balzac,Cousin Bette
Lion Feuchtwanger,Jew Suess
Emile Zola,Nana
Emile Zola,The Belly of Paris
Truman Capote,In Cold blood
Truman Capote,Breakfast at Tiffany
我们验证数据。
这是 MySQL Java 教程。
|