# MySQL Java 教程
> 原文: [http://zetcode.com/db/mysqljava/](http://zetcode.com/db/mysqljava/)
这是 MySQL 数据库的 Java 教程。 它涵盖了使用 JDBC 进行 Java 中 MySQL 编程的基础。 ZetCode 拥有用于 MySQL Java 的完整电子书: [MySQL Java 编程电子书](/ebooks/mysqljava/)。
[Tweet](https://twitter.com/share)
在本教程中,我们使用 MySQL Connector/J 驱动程序。 它是 MySQL 的官方 JDBC 驱动程序。 这些示例是在 Ubuntu Linux 上创建和测试的。 您可能还需要查看 [Java 教程](/lang/java/), [PostgreSQL Java 教程](/db/postgresqljavatutorial/), [Apache Derby 教程](/db/apachederbytutorial/), [MySQL 教程](/databases/mysqltutorial/)或 [Spring JdbcTemplate ZetCode 的教程](/db/jdbctemplate/)。
## JDBC
_JDBC_ 是 Java 编程语言的 API,用于定义客户端如何访问数据库。 它提供了查询和更新数据库中数据的方法。 JDBC 面向关系数据库。 从技术角度来看,API 是`java.sql`包中的一组类。 要将 JDBC 与特定数据库一起使用,我们需要该数据库的 JDBC 驱动程序。
JDBC 是 Java 数据库编程的基石。 今天,它被认为是非常底层的,并且容易出错。 创建了诸如 MyBatis 或`JdbcTemplate`之类的解决方案来减轻 JDBC 编程的负担。 但是,实际上,这些解决方案仍然使用 JDBC。 JDBC 是 Java Standard Edition 平台的一部分。
JDBC 管理以下三个主要的编程活动:
* 连接到数据库;
* 向数据库发送查询和更新语句;
* 检索和处理从数据库接收到的结果以响应查询。
## MySQL Connector/J
为了以 Java 连接到 MySQL,MySQL 提供了 MySQL Connector/J,它是实现 JDBC API 的驱动程序。 MySQL Connector/J 是 JDBC Type 4 驱动程序。 Type 4 表示驱动程序是 MySQL 协议的纯 Java 实现,并且不依赖 MySQL 客户端库。 在本教程中,我们使用 MySQL Connector/J 5.1.41,它是 5.1 生产分支的维护版本。
## 连接字符串
使用连接字符串定义数据库连接。 它包含诸如数据库类型,数据库名称,服务器名称和端口号之类的信息。 它还可能包含用于配置的其他键/值对。 每个数据库都有其自己的连接字符串格式。
以下是 MySQL 连接字符串的语法:
```java
jdbc:mysql://[host1][:port1][,[host2][:port2]]...[/[database]]
[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
```
可以为服务器故障转移设置指定多个主机。 方括号中的项目是可选的。 如果未指定主机,则主机名默认为`localhost`。 如果未指定主机端口,则默认为 MySQL 服务器的默认端口号 3306。
```java
jdbc:mysql://localhost:3306/testdb?useSSL=false
```
这是一个 MySQL 连接字符串的示例。 `jdbc:mysql://`被称为子协议,对于 MySQL 来说是恒定的。 我们在 MySQL 标准端口 3306 上连接到`localhost`。数据库名称为`testdb`。 其他键/值对在问号字符(`?`)之后。 `useSSL=false`告诉 MySQL,将没有安全连接。
## 关于 MySQL 数据库
MySQL 是领先的开源数据库管理系统。 它是一个多用户,多线程的数据库管理系统。 MySQL 在网络上特别流行。 它是由 Linux,Apache,MySQL 和 PHP 组成的非常流行的 _LAMP_ 平台的一部分。 目前,MySQL 由 Oracle 拥有。 在大多数重要的 OS 平台上都可以使用 MySQL 数据库。 它可以在 BSD Unix,Linux,Windows 或 Mac OS 上运行。 维基百科和 YouTube 使用 MySQL。 这些站点每天管理数百万个查询。 MySQL 有两个版本:MySQL 服务器系统和 MySQL 嵌入式系统。
## 设置 MySQL
在本节中,我们将安装 MySQL 服务器,创建`testdb`数据库和测试用户。
```java
$ sudo apt-get install mysql-server
```
此命令将安装 MySQL 服务器和其他各种包。 在安装包时,提示我们输入 MySQL 根帐户的密码。
接下来,我们将创建一个新的数据库用户和一个新的数据库。 我们使用`mysql`客户端。
```java
$ sudo service mysql status
mysql start/running, process 5129
```
我们检查 MySQL 服务器是否正在运行。 如果没有,我们需要启动服务器。 在 Ubuntu Linux 上,可以使用`sudo service mysql start`命令来完成。
```java
$ 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 监视器客户端应用连接到服务器。 我们使用根帐户连接到数据库。 我们用`SHOW DATABASES`语句显示所有可用的数据库。
```java
mysql> CREATE DATABASE testdb;
Query OK, 1 row affected (0.02 sec)
```
我们创建一个新的`testdb`数据库。 在整个教程中,我们将使用此数据库。
```java
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`
```java
<?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 驱动程序的依赖关系。 我们还包括用于执行 Maven Java 程序的`exec-maven-plugin`。 在`<mainClass></mainClass>`标签之间,我们提供了应用的全名。
## Java MySQL 版本
如果以下程序运行正常,则我们已安装一切正常。 我们检查 MySQL 服务器的版本。
`JdbcMySQLVersion.java`
```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 服务器的一些信息。
```java
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false";
```
这是 MySQL 数据库的连接 URL。 每个驱动程序对于 URL 都有不同的语法。 在本例中,我们提供了主机,端口和数据库名称。
```java
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`语法可确保最终清除资源。
```java
if (result.next()) {
System.out.println(result.getString(1));
}
```
`ResultSet`对象维护一个游标,该游标指向其当前数据行。 最初,光标位于第一行之前。 `next()`方法将光标移动到下一行。 如果没有剩余的行,则该方法返回`false`。 `getString()`方法检索指定列的值。 第一列的索引为 1。
```java
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(JdbcMySQLVersion.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
```
如果发生异常,我们将记录错误消息。 对于此控制台示例,该消息显示在终端中。
```java
$ mvn exec:java -q
5.7.21-0ubuntu0.16.04.1
```
我们从命令行运行程序。 Manen 的`-q`选项在安静模式下运行 Maven。 即我们只看到错误消息。
## 创建和填充表
接下来,我们将创建数据库表并用数据填充它们。 这些表将在本教程中使用。
`mysql_tables.sql`
```java
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 数据库支持外键约束和事务。 我们将外键约束放置在`Books`表的`AuthorId`列上。 我们用初始数据填充`Authors`和`Books`表。
```java
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 中,`PreparedStatement`是代表预编译的 SQL 语句的对象。
`JdbcPrepared.java`
```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`表中。
```java
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
```
在这里,我们创建一个预备语句。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预备语句更快,并且可以防止 SQL 注入攻击。 `?`是一个占位符,稍后将被填充。
```java
pst.setString(1, author);
```
值绑定到占位符。
```java
pst.executeUpdate();
```
执行预备语句。 当我们不希望返回任何数据时,我们使用语句对象的`executeUpdate()`方法。 这是当我们创建数据库或执行`INSERT`,`UPDATE`,`DELETE`语句时。
```java
$ 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 预备和非预备的语句
对于以下两个示例,我们将使用`Testing`表。 我们将执行一条普通语句和一条准备语句 5000 次。 我们检查执行时间是否有差异。
`JdbcNotPreparedTesting.java`
```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`对象。
```java
for (int i = 1; i <= 5000; i++) {
String sql = "INSERT INTO Testing(Id) VALUES(" + 2 * i + ")";
st.executeUpdate(sql);
}
```
我们建立查询并执行 5000 次。
```java
$ time mvn exec:java -q
real 4m14.716s
user 0m6.820s
sys 0m0.404s
```
完成 5000 次插入需要 4.14 分钟。
`JdbcPreparedTesting.java`
```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`执行相同的任务。
```java
try (Connection con = DriverManager.getConnection(cs, user, password);
PreparedStatement pst = con.prepareStatement(sql)) {
```
我们使用`prepareStatement()`方法创建预备语句。
```java
for (int i = 1; i <= 5000; i++) {
pst.setInt(1, i * 2);
pst.executeUpdate();
}
```
我们将一个值绑定到预备语句,并在循环中执行数千次。
```java
$ time mvn exec:java -q
real 3m53.962s
user 0m6.280s
sys 0m0.380s
```
现在花了 3.53 分钟完成了 5000 次插入。 我们节省了 20 秒。
## Java MySQL 检索数据
接下来,我们将展示如何从数据库表中检索数据。 我们从`Authors`表中获取所有数据。
`JdbcRetrieve.java`
```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`表中获取所有作者并将其打印到控制台。
```java
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 查询返回的数据表。
```java
while (rs.next()) {
System.out.print(rs.getInt(1));
System.out.print(": ");
System.out.println(rs.getString(2));
}
```
`next()`方法将光标移至下一条记录。 当结果集中没有更多行时,它将返回`false`。 `getInt()`和`getString()`方法检索此`ResultSet`对象当前行中指定列的值,作为 Java 编程语言的`int`和`String`。
```java
$ 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`
```java
db.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
db.user=testuser
db.passwd=test623
```
我们有一个`db.properties`文件,其中有三个键/值对。 这些是在程序执行期间动态加载的。
`JdbcProperties.java`
```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`表的内容打印到控制台。 这次,我们从文件加载连接属性。 他们没有在程序中硬编码。
```java
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`的文件中加载的,其中包含我们的配置数据。
```java
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`
```java
# 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`
```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);
}
}
}
```
在此示例中,我们使用数据源连接到数据库。
```java
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`文件中读取数据库属性。
```java
MysqlDataSource ds = new MysqlDataSource();
ds.setURL(props.getProperty("mysql.url"));
ds.setUser(props.getProperty("mysql.username"));
ds.setPassword(props.getProperty("mysql.password"));
```
创建`MysqlDataSource`并设置数据源属性。
```java
try (Connection con = ds.getConnection();
PreparedStatement pst = con.prepareStatement(query);
ResultSet rs = pst.executeQuery()) {
```
连接对象是从数据源创建的。
## Java MySQL 多条语句
可以在一个查询中执行多个 SQL 语句。 必须将`allowMultiQueries`设置为启用 MySQL 中的多个语句。
`JdbcMulStat.java`
```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`语句来获取三行。
```java
String cs = "jdbc:mysql://localhost:3306/"
+ "testdb?allowMultiQueries=true&useSSL=false";
```
通过将`allowMultiQueries`参数设置为`true`,我们可以在数据库 URL 中启用多个语句查询。
```java
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";
```
在这里,我们有一个包含多个语句的查询。 语句用分号分隔。
```java
boolean isResult = pst.execute();
```
我们调用已预备语句对象的`execute()`方法。 该方法返回一个布尔值,该布尔值指示第一个结果是否为`ResultSet`对象。 使用`getMoreResults()`方法调用后续结果。
```java
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`循环内完成。 通过`getResultSet()`方法调用检索`ResultSet`。 为了找出是否还有其他结果,我们调用`getMoreResults()`方法。
```java
$ mvn exec:java -q
1: Jack London
2: Honore de Balzac
3: Lion Feuchtwanger
```
这是示例的输出。 前三行是从`Authors`表中检索的。
## Java MySQL 列标题
下面的示例显示如何使用数据库表中的数据打印列标题。 我们将列名称称为元数据。 元数据是有关数据库中核心数据的数据。
`JdbcColumnHeaders.java`
```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`表中选择他们的书。 我们打印结果集中返回的列的名称。 输出已格式化。
```java
String query = "SELECT Name, Title From Authors, " +
"Books WHERE Authors.Id=Books.AuthorId";
```
这是将作者与他们的书联系在一起的 SQL 语句。
```java
ResultSetMetaData meta = rs.getMetaData();
```
要获取列名,我们需要获取`ResultSetMetaData`。 它是一个对象,可用于获取有关`ResultSet`对象中列的类型和属性的信息。
```java
String colname1 = meta.getColumnName(1);
String colname2 = meta.getColumnName(2);
```
从获得的元数据中,我们获得列名。
```java
String header = String.format("%-21s%s", colname1, colname2);
System.out.println(header);
```
我们将列名称打印到控制台。
```java
while (rs.next()) {
String row = String.format("%-21s", rs.getString(1));
System.out.print(row);
System.out.println(rs.getString(2));
}
```
我们将数据打印到控制台。 第一列为 21 个字符,并在左侧对齐。
```java
$ 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`
```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。
```java
try (Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
```
第一步,我们必须将`Statement.RETURN_GENERATED_KEYS`传递给`prepareStatement()`方法。
```java
try (ResultSet rs = pst.getGeneratedKeys()) {
```
然后,我们使用`getGeneratedKeys()`方法检索生成的键。
```java
if (rs.first()) {
System.out.printf("The ID of new author: %d", rs.getLong(1));
}
```
由于我们只有一个插入语句,因此我们使用`first()`导航到该值。
```java
$ mvn exec:java -q
The ID of new author: 7
```
这是一个示例输出。
## MySQL Java 编写图像
有些人喜欢将其图像放入数据库中,有些人则希望将其保留在文件系统中以供其应用使用。 当我们处理大量图像时,会出现技术难题。 图像是二进制数据。 MySQL 数据库具有一种特殊的数据类型来存储称为`BLOB`(二进制大对象)的二进制数据。
对于此示例,我们使用`Images`表。
`JdbcWriteImage.java`
```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`表中。
```java
String sql = "INSERT INTO Images(Data) VALUES(?)";
```
这是插入图像的 SQL。
```java
File myFile = new File("src/main/resources/tree.png");
try (FileInputStream fin = new FileInputStream(myFile)) {
```
我们为图像文件创建一个`File`对象。 要从该文件读取字节,我们创建一个`FileInputStream`对象。
```java
pst.setBinaryStream(1, fin, (int) myFile.length());
```
二进制流设置为预备语句。 `setBinaryStream()`方法的参数是要绑定的参数索引,输入流和流中的字节数。
```java
pst.executeUpdate();
```
我们执行该语句。
## MySQL Java 读取图像
在前面的示例中,我们已将图像插入数据库表中。 现在,我们将从表中读取图像。
`JdbcReadImage.java`
```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);
}
}
}
```
我们从图片表中读取了一张图片。
```java
String query = "SELECT Data FROM Images LIMIT 1";
```
我们从表中选择一条记录。
```java
String fileName = "src/main/resources/tree.png";
try (FileOutputStream fos = new FileOutputStream(fileName)) {
```
创建`FileOutputStream`对象以写入文件。 它旨在写入原始字节流,例如图像数据。
```java
Blob blob = result.getBlob("Data");
```
我们通过调用`getBlob()`方法从`Data`列中获取图像数据。
```java
int len = (int) blob.length();
```
我们计算出斑点数据的长度。 换句话说,我们得到字节数。
```java
byte[] buf = blob.getBytes(1, len);
```
`getBytes()`方法以字节数组的形式检索 Blob 对象的所有字节。
```java
fos.write(buf, 0, len);
```
字节被写入输出流。 该映像在文件系统上创建。
## 事务支持
事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。
MySQL 数据库具有不同类型的存储引擎。 最常见的是 MyISAM 和 InnoDB 引擎。 在数据安全性和数据库速度之间需要权衡。 MyISAM 表的处理速度更快,并且不支持事务。 另一方面,InnoDB 表可以更安全地防止数据丢失。 它们支持事务,并且处理速度较慢。
`JdbcTransaction.java`
```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`表的第一行上更改作者的姓名。 我们还必须更改与该作者相关的书籍。 这是一个需要进行事务的很好的例子。 如果我们更改作者但不更改作者的书,则数据已损坏。
```java
con.setAutoCommit(false);
```
要处理事务,我们必须将自动提交模式设置为`false`。 默认情况下,数据库连接处于自动提交模式。 在这种模式下,每条语句在执行后都会立即提交给数据库。 声明无法撤消。 当自动提交关闭时,我们通过调用`commit()`提交更改,或通过调用`rollback()`方法将其回滚。
```java
st.executeUpdate("UPDATE Books SET Titl = 'Anna Karenina' "
+ "WHERE Id = 2");
```
第三个 SQL 语句有一个错误。 表中没有`Titl`栏。
```java
con.commit();
```
如果没有异常,则提交事务。
```java
try {
con.rollback();
} catch (SQLException ex1) {
Logger lgr = Logger.getLogger(JdbcTransaction.class.getName());
lgr.log(Level.WARNING, ex1.getMessage(), ex1);
}
```
发生异常时,事务将回滚。 没有更改提交到数据库。
```java
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'
```
该应用以异常结束。
```java
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`
```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);
}
}
}
```
我们有同样的例子。 这次,没有事务支持。
```java
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)
```
再次引发异常。 列夫·托尔斯泰没有写马丁·伊甸园; 数据已损坏。
## 批量更新
当我们需要使用多个语句更新数据时,可以使用批处理更新。 批处理更新可用于`INSERT`,`UPDATE`,`DELETE`语句以及`CREATE TABLE`和`DROP TABLE`语句。
`JdbcBatchUpdate.java`
```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`,以查看更改。
```java
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')");
...
```
我们使用`addBatch()`方法向该语句添加新命令。
```java
int counts[] = st.executeBatch();
```
添加所有命令后,我们调用`executeBatch()`进行批量更新。 该方法返回已提交更改的数组。
```java
con.commit();
```
批处理更新在事务中提交。
```java
} 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()`。
```java
$ 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`具有适当的文件许可权; 否则,我们会收到拒绝访问错误消息。
```java
mysql> GRANT FILE ON *.* TO 'testuser'@'localhost';
```
我们设置了`FILE`权限。
```java
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`
```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`文件中。
```java
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 语句。
```java
$ 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 教程。 您可能也对 [JDBI 教程](/db/jdbi/), [Java H2 教程](/java/h2database/), [PostgreSQL Java 教程](/db/postgresqljavatutorial/), [MongoDB Java 教程](/db/mongodbjava/)或 [MySQL 教程](/databases/mysqltutorial/)感兴趣。
- ZetCode 数据库教程
- MySQL 教程
- MySQL 简介
- MySQL 安装
- MySQL 的第一步
- MySQL 快速教程
- MySQL 存储引擎
- MySQL 数据类型
- 在 MySQL 中创建,更改和删除表
- MySQL 表达式
- 在 MySQL 中插入,更新和删除数据
- MySQL 中的SELECT语句
- MySQL 子查询
- MySQL 约束
- 在 MySQL 中导出和导入数据
- 在 MySQL 中连接表
- MySQL 函数
- MySQL 中的视图
- MySQL 中的事务
- MySQL 存储过程
- MySQL Python 教程
- MySQL Perl 教程
- MySQL & Perl DBI
- 使用 Perl 连接到 MySQL 数据库
- MySQL 中的 Perl 错误处理
- 使用 Perl 进行 MySQL 查询
- 在 MySQL 中使用 Perl 绑定参数&列
- 在 MySQL 中使用 Perl 处理图像
- 使用 Perl 获取 MySQL 元数据
- Perl 的 MySQL 事务
- MySQL C API 编程教程
- MySQL Visual Basic 教程
- MySQL PHP 教程
- MySQL Java 教程
- MySQL Ruby 教程
- MySQL C# 教程
- SQLite 教程
- SQLite 简介
- sqlite3 命令行工具
- 在 SQLite 中创建,删除和更改表
- SQLite 表达式
- SQLite 插入,更新,删除数据
- SQLite SELECT语句
- SQLite 约束
- SQLite 连接表
- SQLite 函数
- SQLite 视图,触发器,事务
- SQLite C 教程
- SQLite Python 教程
- SQLite Perl 教程
- Perl DBI
- 使用 Perl 连接到 SQLite 数据库
- SQLite Perl 错误处理
- 使用 Perl 的 SQLite 查询
- 使用 Perl 绑定 SQLite 参数&列
- 使用 Perl 在 SQLite 中处理图像
- 使用 Perl 获取 SQLite 元数据
- 使用 Perl 进行 SQLite 事务
- SQLite Ruby 教程
- 连接到 SQLite 数据库
- 在 SQLite 中使用 Ruby 进行 SQL 查询
- 绑定参数
- 处理图像
- 使用 Ruby 获取 SQLite 元数据
- Ruby 的 SQLite 事务
- SQLite C# 教程
- SQLite C# 简介
- 使用SqliteDataReader检索数据
- ADO.NET 数据集
- 使用 C# 在 SQLite 中处理图像
- 使用 C# 获取 SQLite 元数据
- 使用 C# 的 SQLite 事务
- SQLite Visual Basic 教程
- SQLite Visual Basic 简介
- 使用SqliteDataReader检索数据
- ADO.NET 的数据集
- 使用 Visual Basic 在 SQLite 中处理图像
- 使用 Visual Basic 获取 SQLite 元数据
- 使用 Visual Basic 的 SQLite 事务
- PostgreSQL C 教程
- PostgreSQL Ruby 教程
- PostgreSQL PHP 教程
- PostgreSQL PHP 编程简介
- 在 PostgreSQL 中使用 PHP 检索数据
- 在 PostgreSQL 中使用 PHP 处理图像
- 用 PHP 获取 PostgreSQL 元数据
- 在 PostgreSQL 中使用 PHP 进行事务
- PostgreSQL Java 教程
- Apache Derby 教程
- Derby 简介
- Derby 的安装&配置
- Derby 工具
- ij 工具
- Derby 中的 SQL 查询
- 在 Derby 中使用 JDBC 进行编程
- Derby 安全
- 使用 Derby & Apache Tomcat
- NetBeans 和 Derby
- SQLAlchemy 教程
- SQLAlchemy 简介
- 原始 SQL
- 模式定义语言
- SQL 表达式语言
- SQLAlchemy 中的对象关系映射器
- MongoDB PHP 教程
- MongoDB JavaScript 教程
- MongoDB Ruby 教程
- Spring JdbcTemplate 教程
- JDBI 教程
- MyBatis 教程
- Hibernate Derby 教程
- ZetCode .NET 教程
- Visual Basic 教程
- Visual Basic
- Visual Basic 语法结构
- 基本概念
- Visual Basic 数据类型
- Visual Basic 中的字符串
- 运算符
- 控制流
- Visual Basic 数组
- Visual Basic 中的过程&函数
- 在 Visual Basic 中组织代码
- 面向对象编程
- Visual Basic 中的面向对象编程 II
- Visual Basic 中的集合
- 输入和输出
- C# 教程
- C# 语言
- C# 语法结构
- C# 基础
- C# 数据类型
- C# 中的字符串
- C# 运算符
- C# 中的流控制
- C# 数组
- C# 面向对象编程
- C# 中的方法
- C# 面向对象编程 II
- C# 属性
- C# 结构
- C# 委托
- 命名空间
- C# 集合
- C# 输入和输出
- C# 目录教程
- C# 字典教程
- 在 C# 中读取文本文件
- C# 中的日期和时间
- 在 C# 中读取网页
- C# HttpClient教程
- ASP.NET Core 教程
- ZetCode 图形教程
- Java 2D 游戏教程
- Java 游戏基础
- 动画
- 移动精灵
- 碰撞检测
- Java 益智游戏
- Java Snake
- Breakout 游戏
- Java 俄罗斯方块
- Java 吃豆人
- Java 太空侵略者
- Java 扫雷
- Java 推箱子
- Java 2D 教程
- 介绍
- 基本绘图
- 形状和填充
- 透明度
- 合成
- 剪裁
- 变换
- 特效
- 图像
- 文字和字体
- 命中测试,移动物体
- 俄罗斯方块
- Cario 图形教程
- Cario 图形库
- Cario 定义
- Cairo 后端
- Cairo 基本图形
- 形状和填充
- 渐变
- 透明度
- 合成
- 剪裁和遮罩
- 变换
- Cairo 文字
- Cairo 中的图像
- 根窗口
- PyCairo 教程
- PyCairo 简介
- PyCairo 后端
- PyCairo 中的基本绘图
- PyCairo 形状和填充
- PyCairo 渐变
- PyCairo 剪裁&遮罩
- PyCairo 的透明度
- PyCairo 中的变换
- PyCairo 中的文字
- PyCairo 中的图像
- 根窗口
- HTML5 画布教程
- 介绍
- HTML5 画布中的直线
- HTML5 画布形状
- HTML5 画布填充
- HTML5 画布中的透明度
- HTML5 画布合成
- HTML5 canvas 中的变换
- HTML5 画布中的文字
- HTML5 画布中的动画
- HTML5 画布中的 Snake
- ZetCode GUI 教程
- Windows API 教程
- Windows API 简介
- Windows API main函数
- Windows API 中的系统函数
- Windows API 中的字符串
- Windows API 中的日期和时间
- Windows API 中的一个窗口
- UI 的第一步
- Windows API 菜单
- Windows API 对话框
- Windows API 控件 I
- Windows API 控件 II
- Windows API 控件 III
- Windows API 中的高级控件
- Windows API 中的自定义控件
- Windows API 中的 GDI
- PyQt4 教程
- PyQt4 简介
- PyQt4 中的第一个程序
- PyQt4 中的菜单和工具栏
- PyQt4 中的布局管理
- PyQt4 中的事件和信号
- PyQt4 中的对话框
- PyQt4 小部件
- PyQt4 小部件 II
- PyQt4 中的拖放
- PyQt4 中的绘图
- PyQt4 中的自定义小部件
- PyQt4 中的俄罗斯方块游戏
- PyQt5 教程
- PyQt5 简介
- PyQt5 日期和时间
- PyQt5 中的第一个程序
- PyQt5 中的菜单和工具栏
- PyQt5 中的布局管理
- PyQt5 中的事件和信号
- PyQt5 中的对话框
- PyQt5 小部件
- PyQt5 小部件 II
- PyQt5 拖放
- PyQt5 中的绘图
- PyQt5 中的自定义小部件
- PyQt5 中的俄罗斯方块
- Qt4 教程
- Qt4 工具包简介
- Qt4 工具类
- Qt4 中的字符串
- Qt4 中的日期和时间
- 在 Qt4 中使用文件和目录
- Qt4 中的第一个程序
- Qt4 中的菜单和工具栏
- Qt4 中的布局管理
- Qt4 中的事件和信号
- Qt4 小部件
- Qt4 小部件 II
- Qt4 中的绘图
- Qt4 中的自定义小部件
- Qt4 中的打砖块游戏
- Qt5 教程
- Qt5 工具包简介
- Qt5 中的字符串
- Qt5 中的日期和时间
- Qt5 中的容器
- 在 Qt5 中处理文件和目录
- Qt5 中的第一个程序
- Qt5 中的菜单和工具栏
- Qt5 中的布局管理
- Qt5 中的事件和信号
- Qt5 小部件
- Qt5 小部件 II
- Qt5 中的绘图
- Qt5 中的自定义小部件
- Qt5 中的贪食蛇
- Qt5 中的打砖块游戏
- PySide 教程
- PySide 工具包简介
- PySide 中的第一个程序
- PySide 中的菜单和工具栏
- PySide 中的布局管理
- PySide 中的事件和信号
- PySide 中的对话框
- PySide 小部件
- PySide 小部件 II
- 在 PySide 中拖放
- 在 PySide 中绘图
- PySide 中的自定义小部件
- PySide 中的俄罗斯方块游戏
- Tkinter 教程
- Tkinter 简介
- Tkinter 中的布局管理
- Tkinter 标准小部件属性
- Tkinter 小部件
- Tkinter 中的菜单和工具栏
- Tkinter 中的对话框
- Tkinter 中的绘图
- Tkinter 中的贪食蛇
- Tcl/Tk 教程
- Tcl/Tk 简介
- Tcl/Tk 中的布局管理
- Tcl/Tk 小部件
- Tcl/Tk 中的菜单和工具栏
- Tcl/Tk 中的对话框
- Tcl/Tk 绘图
- 贪食蛇
- Qt 快速教程
- Java Swing 教程
- Java Swing 简介
- Java Swing 首个程序
- Java Swing 中的菜单和工具栏
- Swing 布局管理
- GroupLayout管理器
- Java Swing 事件
- 基本的 Swing 组件
- 基本的 Swing 组件 II
- Java Swing 对话框
- Java Swing 模型架构
- Swing 中的拖放
- Swing 中的绘图
- Java Swing 中的可调整大小的组件
- Java Swing 中的益智游戏
- 俄罗斯方块
- JavaFX 教程
- JavaFX 简介
- JavaFX 首个程序
- JavaFX 布局窗格
- 基本的 JavaFX 控件
- 基本 JavaFX 控件 II
- JavaFX 事件
- JavaFX 效果
- JavaFX 动画
- JavaFX 画布
- JavaFX 图表
- Java SWT 教程
- Java SWT 简介
- Java SWT 中的布局管理
- Java SWT 中的菜单和工具栏
- Java SWT 中的小部件
- Table小部件
- Java SWT 中的对话框
- Java SWT 绘图
- Java SWT 中的贪食蛇
- wxWidgets 教程
- wxWidgets 简介
- wxWidgets 助手类
- wxWidgets 中的第一个程序
- wxWidgets 中的菜单和工具栏
- wxWidgets 中的布局管理
- wxWidgets 中的事件
- wxWidgets 中的对话框
- wxWidgets 小部件
- wxWidgets 小部件 II
- wxWidgets 中的拖放
- wxWidgets 中的设备上下文
- wxWidgets 中的自定义小部件
- wxWidgets 中的俄罗斯方块游戏
- wxPython 教程
- wxPython 简介
- 第一步
- 菜单和工具栏
- wxPython 中的布局管理
- wxPython 中的事件
- wxPython 对话框
- 小部件
- wxPython 中的高级小部件
- wxPython 中的拖放
- wxPython 图形
- 创建自定义小部件
- wxPython 中的应用框架
- wxPython 中的俄罗斯方块游戏
- C# Winforms Mono 教程
- Mono Winforms 简介
- Mono Winforms 中的第一步
- Mono Winforms 中的布局管理
- Mono Winforms 中的菜单和工具栏
- Mono Winforms 中的基本控件
- Mono Winforms 中的高级控件
- 对话框
- Mono Winforms 中的拖放
- Mono Winforms 中的绘图
- Mono Winforms 中的贪食蛇
- Java Gnome 教程
- Java Gnome 简介
- Java Gnome 的第一步
- Java Gnome 中的布局管理
- Java Gnome 中的布局管理 II
- Java Gnome 中的菜单
- Java Gnome 中的工具栏
- Java Gnome 中的事件
- Java Gnome 中的小部件
- Java Gnome 中的小部件 II
- Java Gnome 中的高级小部件
- Java Gnome 中的对话框
- Java Gnome 中的 Pango
- 在 Java Gnome 中用 Cairo 绘图
- Cario 绘图 II
- Java Gnome 中的贪食蛇
- QtJambi 教程
- QtJambi 简介
- QtJambi 中的布局管理
- QtJambi 中的小部件
- QtJambi 中的菜单和工具栏
- QtJambi 对话框
- QtJambi 中的绘图
- QtJambi 中的自定义小部件
- 贪食蛇
- GTK+ 教程
- GTK+ 简介
- GTK+ 中的第一个程序
- GTK+ 中的菜单和工具栏
- GTK+ 布局管理
- GTK+ 事件和信号
- GTK+ 对话框
- GTK+ 小部件
- GTK+ 小部件 II
- GtkTreeView小部件
- GtkTextView小部件
- 自定义 GTK+ 小部件
- Ruby GTK 教程
- Ruby GTK 简介
- Ruby GTK 中的布局管理
- Ruby GTK 中的小部件
- Ruby GTK 中的菜单和工具栏
- Ruby GTK 中的对话框
- Ruby GTK Cario 绘图
- Ruby GTK 中的自定义小部件
- Ruby GTK 中的贪食蛇
- GTK# 教程
- GTK# 简介
- GTK 的第一步
- GTK# 中的布局管理
- GTK 中的菜单
- GTK# 中的工具栏
- GTK# 中的事件
- GTK# 中的小部件
- GTK 中的小部件 II
- GTK# 中的高级小部件
- GTK# 中的对话框
- Pango
- GTK# 中的 Cario 绘图
- GTK# 中的 Cario 绘图 II
- GTK# 中的自定义小部件
- Visual Basic GTK# 教程
- Visual Basic GTK# 简介
- 布局管理
- 小部件
- 菜单和工具栏
- 对话框
- Cario 绘图
- 自定义小部件
- 贪食蛇
- PyGTK 教程
- PyGTK 简介
- PyGTK 的第一步
- PyGTK 中的布局管理
- PyGTK 中的菜单
- PyGTK 中的工具栏
- PyGTK 中的事件和信号
- PyGTK 中的小部件
- PyGTK 中的小部件 II
- PyGTK 中的高级小部件
- PyGTK 中的对话框
- Pango
- Pango II
- PyGTK 中的 Cario 绘图
- Cario 绘图 II
- PyGTK 中的贪食蛇游戏
- PyGTK 中的自定义小部件
- PHP GTK 教程
- PHP GTK 简介
- PHP GTK 中的布局管理
- PHP GTK 中的小部件
- PHP GTK 中的菜单和工具栏
- 对话框
- Cario 绘图
- 自定义小部件
- 贪食蛇
- C# Qyoto 教程
- Qyoto 介绍
- 布局管理
- Qyoto 中的小部件
- Qyoto 中的菜单和工具栏
- Qyoto 对话框
- Qyoto 中的绘图
- Qyoto 中的绘图 II
- Qyoto 中的自定义小部件
- 贪食蛇
- Ruby Qt 教程
- Ruby Qt 简介
- Ruby Qt 中的布局管理
- Ruby Qt 中的小部件
- 菜单和工具栏
- Ruby Qt 中的对话框
- 用 Ruby Qt 绘图
- Ruby Qt 中的自定义小部件
- Ruby Qt 中的贪食蛇
- Visual Basic Qyoto 教程
- Qyoto 介绍
- 布局管理
- Qyoto 中的小部件
- Qyoto 中的菜单和工具栏
- Qyoto 对话框
- Qyoto 中的绘图
- Qyoto 中的自定义小部件
- 贪食蛇
- Mono IronPython Winforms 教程
- 介绍
- IronPython Mono Winforms 中的第一步
- 布局管理
- 菜单和工具栏
- Mono Winforms 中的基本控件
- Mono Winforms 中的基本控件 II
- Mono Winforms 中的高级控件
- 对话框
- Mono Winforms 中的拖放
- 绘图
- IronPython Mono Winforms 中的绘图 II
- IronPython Mono Winforms 中的贪食蛇
- IronPython Mono Winforms 中的俄罗斯方块游戏
- FreeBASIC GTK 教程
- Jython Swing 教程
- Jython Swing 简介
- Jython Swing 中的布局管理
- Jython Swing 中的组件
- Jython Swing 中的菜单和工具栏
- Jython Swing 中的对话框
- Jython Swing 中的绘图
- Jython Swing 中的半字节
- JRuby Swing 教程
- JRuby Swing 简介
- JRuby Swing 中的布局管理
- JRuby Swing 中的组件
- 菜单和工具栏
- JRuby Swing 中的对话框
- 在 JRuby Swing 中绘图
- JRuby Swing 中的贪食蛇
- Visual Basic Winforms 教程
- Visual Basic Winforms 简介
- 布局管理
- 基本控制
- 进阶控件
- 菜单和工具栏
- 对话框
- 绘图
- 拖放
- 贪食蛇
- JavaScript GTK 教程
- JavaScript GTK 简介
- 布局管理
- JavaScript GTK 中的小部件
- JavaScript GTK 中的菜单和工具栏
- JavaScript GTK 中的对话框
- JavaScript GTK 中的 Cario 绘图
- ZetCode Java 教程
- Java 教程
- Java 语言
- Java 语法结构
- Java 基础
- Java 数据类型
- Java 数据类型 II
- Java 字符串
- Java 数组
- Java 表达式
- Java 控制流程
- Java 面向对象的编程
- Java 方法
- Java 面向对象编程 II
- Java 包
- Java 中的异常
- Java 集合
- Java 流
- Java Future 教程
- Java Comparable和Comparator
- Java DOM 教程
- Java MVC 教程
- Java SAX 教程
- Java JAXB 教程
- Java JSON 处理教程
- Java H2 教程
- MongoDB Java 教程
- Java 正则表达式教程
- Java PDFBox 教程
- Java 文件教程
- Java Files.list教程
- Java Files.walk教程
- Java DirectoryStream教程
- Java 外部与内部迭代器
- Java 文件大小
- 用 Java 创建目录
- 用 Java 创建文件
- Java Log4j 教程
- Gson 教程
- Java RequestDispatcher
- Java HTTP GET/POST 请求
- Java InputStream教程
- Java FileOutputStream教程
- Java FileInputStream教程
- Java ZipInputStream教程
- Java FileWriter教程
- EJB 简介
- Java forEach教程
- Jetty 教程
- Tomcat Derby 教程
- Stripes 介绍
- 使用 Stripes 的 Java webapp,MyBatis,& Derby
- EclipseLink 简介
- Java 中的数据源
- JSTL 中的 SQL 查询标记
- Java 验证过滤器
- Hibernate 验证器
- 用 Java 显示图像
- Play 框架简介
- Spark Java 简介
- Java ResourceBundle教程
- Jtwig 教程
- Java Servlet 教程
- Java 套接字教程
- FreeMarker 教程
- Android 教程
- Java EE 5 教程
- JSoup 教程
- JFreeChart 教程
- ImageIcon教程
- 用 Java 复制文件
- Java 文件时间教程
- 如何使用 Java 获取当前日期时间
- Java 列出目录内容
- Java 附加到文件
- Java ArrayList教程
- 用 Java 读写 ICO 图像
- Java int到String的转换
- Java HashSet教程
- Java HashMap教程
- Java static关键字
- Java 中的HashMap迭代
- 用 Java 过滤列表
- 在 Java 中读取网页
- Java 控制台应用
- Java 集合的便利工厂方法
- Google Guava 简介
- OpenCSV 教程
- 用 Java8 的StringJoiner连接字符串
- Java 中元素迭代的历史
- Java 谓词
- Java StringBuilder
- Java 分割字串教学
- Java NumberFormat
- Java TemporalAdjusters教程
- Apache FileUtils教程
- Java Stream 过滤器
- Java 流归约
- Java 流映射
- Java InputStreamReader教程
- 在 Java 中读取文本文件
- Java Unix 时间
- Java LocalTime
- Java 斐波那契
- Java ProcessBuilder教程
- Java 11 的新功能
- ZetCode JavaScript 教程
- Ramda 教程
- Lodash 教程
- Collect.js 教程
- Node.js 简介
- Node HTTP 教程
- Node-config 教程
- Dotenv 教程
- Joi 教程
- Liquid.js 教程
- faker.js 教程
- Handsontable 教程
- PouchDB 教程
- Cheerio 教程
- Axios 教程
- Jest 教程
- JavaScript 正则表达式
- 用 JavaScript 创建对象
- Big.js 教程
- Moment.js 教程
- Day.js 教程
- JavaScript Mustache 教程
- Knex.js 教程
- MongoDB JavaScript 教程
- Sequelize 教程
- Bookshelf.js 教程
- Node Postgres 教程
- Node Sass 教程
- Document.querySelector教程
- Document.all教程
- JSON 服务器教程
- JavaScript 贪食蛇教程
- JavaScript 构建器模式教程
- JavaScript 数组
- XMLHttpRequest教程
- 从 JavaScript 中的 URL 读取 JSON
- 在 JavaScript 中循环遍历 JSON 数组
- jQuery 教程
- Google 图表教程
- ZetCode Kotlin 教程
- Kotlin Hello World 教程
- Kotlin 变量
- Kotlin 的运算符
- Kotlin when表达式
- Kotlin 数组
- Kotlin 范围
- Kotlin Snake
- Kotlin Swing 教程
- Kotlin 字符串
- Kotlin 列表
- Kotlin 映射
- Kotlin 集合
- Kotlin 控制流程
- Kotlin 写入文件
- Kotlin 读取文件教程
- Kotlin 正则表达式
- ZetCode 其它教程
- TCL 教程
- Tcl
- Tcl 语法结构
- Tcl 中的基本命令
- Tcl 中的表达式
- Tcl 中的控制流
- Tcl 中的字符串
- Tcl 列表
- Tcl 中的数组
- Tcl 中的过程
- 输入&输出
- AWK 教程
- Vaadin 教程
- Vaadin 框架介绍
- Vaadin Grid教程
- Vaadin TextArea教程
- Vaadin ComboBox教程
- Vaadin Slider教程
- Vaadin CheckBox教程
- Vaadin Button教程
- Vaadin DateField教程
- Vaadin Link教程
- ZetCode PHP 教程
- PHP 教程
- PHP
- PHP 语法结构
- PHP 基础
- PHP 数据类型
- PHP 字符串
- PHP 运算符
- PHP 中的控制流
- PHP 数组
- PHP 数组函数
- PHP 中的函数
- PHP 正则表达式
- PHP 中的面向对象编程
- PHP 中的面向对象编程 II
- PHP Carbon 教程
- PHP Monolog 教程
- PHP 配置教程
- PHP Faker 教程
- Twig 教程
- Valitron 教程
- Doctrine DBAL QueryBuilder 教程
- PHP Respect 验证教程
- PHP Rakit 验证教程
- PHP PDO 教程
- CakePHP 数据库教程
- PHP SQLite3 教程
- PHP 文件系统函数
- ZetCode Python 教程
- Python 教程
- Python 语言
- 交互式 Python
- Python 语法结构
- Python 数据类型
- Python 字符串
- Python 列表
- Python 字典
- Python 运算符
- Python 关键字
- Python 函数
- Python 中的文件
- Python 中的面向对象编程
- Python 模块
- Python 中的包
- Python 异常
- Python 迭代器和生成器
- Python 内省
- Python Faker 教程
- Python f 字符串教程
- Python bcrypt 教程
- Python 套接字教程
- Python smtplib教程
- OpenPyXL 教程
- Python pathlib教程
- Python YAML 教程
- Python 哈希教程
- Python ConfigParser教程
- Python 日志教程
- Python argparse 教程
- Python SQLite 教程
- Python Cerberus 教程
- Python PostgreSQL 教程
- PyMongo 教程
- PyMySQL 教程
- Peewee 教程
- pyDAL 教程
- pytest 教程
- Bottle 教程
- Python Jinja 教程
- PrettyTable 教程
- BeautifulSoup 教程
- pyquery 教程
- Python for循环
- Python 反转
- Python Lambda 函数
- Python 集合
- Python 映射
- Python CSV 教程-读写 CSV
- Python 正则表达式
- Python SimpleJson 教程
- SymPy 教程
- Pandas 教程
- Matplotlib 教程
- Pillow 教程
- Python FTP 教程
- Python Requests 教程
- Python Arrow 教程
- Python 列表推导式
- Python 魔术方法
- PyQt 中的QPropertyAnimation
- PyQt 中的QNetworkAccessManager
- ZetCode Ruby 教程
- Ruby 教程
- Ruby
- Ruby 语法结构
- Ruby 基础
- Ruby 变量
- Ruby 中的对象
- Ruby 数据类型
- Ruby 字符串
- Ruby 表达式
- Ruby 控制流
- Ruby 数组
- Ruby 哈希
- Ruby 中的面向对象编程
- Ruby 中的面向对象编程 II
- Ruby 正则表达式
- Ruby 输入&输出
- Ruby HTTPClient教程
- Ruby Faraday 教程
- Ruby Net::HTTP教程
- ZetCode Servlet 教程
- 从 Java Servlet 提供纯文本
- Java Servlet JSON 教程
- Java Servlet HTTP 标头
- Java Servlet 复选框教程
- Java servlet 发送图像教程
- Java Servlet JQuery 列表教程
- Servlet FreeMarker JdbcTemplate 教程-CRUD 操作
- jQuery 自动补全教程
- Java servlet PDF 教程
- servlet 从 WAR 内读取 CSV 文件
- Java HttpServletMapping
- EasyUI datagrid
- Java Servlet RESTFul 客户端
- Java Servlet Log4j 教程
- Java Servlet 图表教程
- Java ServletConfig教程
- Java Servlet 读取网页
- 嵌入式 Tomcat
- Java Servlet 分页
- Java Servlet Weld 教程
- Java Servlet 上传文件
- Java Servlet 提供 XML
- Java Servlet 教程
- JSTL forEach标签
- 使用 jsGrid 组件
- ZetCode Spring 教程
- Spring @Bean注解教程
- Spring @Autowired教程
- Spring @GetMapping教程
- Spring @PostMapping教程
- Spring @DeleteMapping教程
- Spring @RequestMapping教程
- Spring @PathVariable教程
- Spring @RequestBody教程
- Spring @RequestHeader教程
- Spring Cookies 教程
- Spring 资源教程
- Spring 重定向教程
- Spring 转发教程
- Spring ModelAndView教程
- Spring MessageSource教程
- Spring AnnotationConfigApplicationContext
- Spring BeanFactoryPostProcessor教程
- Spring BeanFactory教程
- Spring context:property-placeholder教程
- Spring @PropertySource注解教程
- Spring @ComponentScan教程
- Spring @Configuration教程
- Spring C 命名空间教程
- Spring P 命名空间教程
- Spring bean 引用教程
- Spring @Qualifier注解教程
- Spring ClassPathResource教程
- Spring 原型作用域 bean
- Spring Inject List XML 教程
- Spring 概要文件 XML 教程
- Spring BeanDefinitionBuilder教程
- Spring 单例作用域 bean
- 独立的 Spring 应用
- 经典 Spring 应用中的JdbcTemplate
- Spring EmbeddedDatabaseBuilder教程
- Spring HikariCP 教程
- Spring Web 应用简介
- Spring BeanPropertyRowMapper教程
- Spring DefaultServlet教程
- Spring WebSocket 教程
- Spring WebJars 教程
- Spring @MatrixVariable教程
- Spring Jetty 教程
- Spring 自定义 404 错误页面教程
- Spring WebApplicationInitializer教程
- Spring BindingResult教程
- Spring FreeMarker 教程
- Spring Thymeleaf 教程
- Spring ResourceHandlerRegistry教程
- SpringRunner 教程
- Spring MockMvc 教程
- ZetCode Spring Boot 教程
- Spring Boot 发送电子邮件教程
- Spring Boot WebFlux 教程
- Spring Boot ViewControllerRegistry教程
- Spring Boot CommandLineRunner教程
- Spring Boot ApplicationReadyEvent 教程
- Spring Boot CORS 教程
- Spring Boot @Order教程
- Spring Boot @Lazy教程
- Spring Boot Flash 属性
- Spring Boot CrudRepository 教程
- Spring Boot JpaRepository 教程
- Spring Boot findById 教程
- Spring Boot Data JPA @NamedQuery教程
- Spring Boot Data JPA @Query教程
- Spring Boot Querydsl 教程
- Spring Boot Data JPA 排序教程
- Spring Boot @DataJpaTest教程
- Spring Boot TestEntityManager 教程
- Spring Boot Data JPA 派生的查询
- Spring Boot Data JPA 查询示例
- Spring Boot Jersey 教程
- Spring Boot CSV 教程
- SpringBootServletInitializer教程
- 在 Spring Boot 中加载资源
- Spring Boot H2 REST 教程
- Spring Boot RestTemplate
- Spring Boot REST XML 教程
- Spring Boot Moustache 教程
- Spring Boot Thymeleaf 配置
- Spring Boot 自动控制器
- Spring Boot FreeMarker 教程
- Spring Boot Environment
- Spring Boot Swing 集成教程
- 在 Spring Boot 中提供图像文件
- 在 Spring Boot 中创建 PDF 报告
- Spring Boot 基本注解
- Spring Boot @ResponseBody教程
- Spring Boot @PathVariable教程
- Spring Boot REST Data JPA 教程
- Spring Boot @RequestParam教程
- Spring Boot 列出 bean
- Spring Boot @Bean
- Spring Boot @Qualifier教程
- 在 Spring Boot 中提供静态内容
- Spring Boot Whitelabel 错误
- Spring Boot DataSourceBuilder 教程
- Spring Boot H2 教程
- Spring Boot Web JasperReports 集成
- Spring Boot iText 教程
- Spring Boot cmd JasperReports 集成
- Spring Boot RESTFul 应用
- Spring Boot 第一个 Web 应用
- Spring Boot Groovy CLI
- Spring Boot 上传文件
- Spring Boot @ExceptionHandler
- Spring Boot @ResponseStatus
- Spring Boot ResponseEntity
- Spring Boot @Controller
- Spring Boot @RestController
- Spring Boot @PostConstruct
- Spring Boot @Component
- Spring Boot @ConfigurationProperties教程
- Spring Boot @Repository
- Spring Boot MongoDB 教程
- Spring Boot MongoDB Reactor 教程
- Spring Boot PostgreSQL 教程
- Spring Boot @ModelAttribute
- Spring Boot 提交表单教程
- Spring Boot Model
- Spring Boot MySQL 教程
- Spring Boot GenericApplicationContext
- SpringApplicationBuilder教程
- Spring Boot Undertow 教程
- Spring Boot 登录页面教程
- Spring Boot RouterFunction 教程
- ZetCode Symfony 教程
- Symfony DBAL 教程
- Symfony 表单教程
- Symfony CSRF 教程
- Symfony Vue 教程
- Symfony 简介
- Symfony 请求教程
- Symfony HttpClient教程
- Symfony Flash 消息
- 在 Symfony 中发送邮件
- Symfony 保留表单值
- Symfony @Route注解教程
- Symfony 创建路由
- Symfony 控制台命令教程
- Symfony 上传文件
- Symfony 服务教程
- Symfony 验证教程
- Symfony 翻译教程