本文都是以MySQL为例,因为学习的时候只下载了MySQL数据库。
根据《Java 核心技术 卷2 高级特效》的顺序来学习,本文的很多实例也都是从书中直接搬过来的。
JDBC介绍
JDBC(Java DataBase Connectivity):java数据库连接。
JDBC是一套单纯的Java API,不同的数据库提供自己独有的驱动程序,将其插入到驱动管理器中。所有通过这个API编写的程序都可以与驱动管理器进行通信。这就意味着,如果程序员想要使用数据库,就不得不使用JDBC API。
数据库的简单介绍
可以将数据库想象成多个行列表格组成的表集,可以具象为如下表格
Author_ID | Name | Fname |
---|---|---|
ALEX | Alexander | Cristopher |
BROO | Brooks | Frederick P. |
… | … | … |
Title | ISBN | Publisher_ID | Price |
---|---|---|---|
A Guide to the SQL Standard | 0-201-96426 | 0201 | 47.95 |
APattern Language: Towns,Buildings, Construction | 0-19-501919-9 | 019 | 65.00 |
… | … | … | … |
ISBN | Author_ID | Seq_No |
---|---|---|
0-201-96426-0 | DATE | 1 |
0-201-96426 | DARW | 2 |
0-19-501919-9 | ALEX | 1 |
… | … | … |
Publisher_ID | Name | URL |
---|---|---|
0201 | Addison-Wesley | www.aw-bc.com |
0407 | John Wiley & Sons | www.wiley.com |
… | … | … |
…
可以通过工具查看数据库中的数据,在没有安装工具的情况下可以通过cmd执行SQL语句查看数据库中的数据。按照惯例,SQL关键字全部使用大写字母,当然也可以不这么做。
1.查询Books表格中的数据,*代指所有的意思,查询语句中FROM是必不可少的,它指定查找源
SELECT * FROM Books
2.查询Books表格中的具体数据
SELECT ISBN, Price, Title FROM Books
3.还可以在查询语句中使用WHERE语句定义想要查找的对象需要满足的条件,需要注意的一点是,SQL使用 = 和 <> 表示等于和不等于,而非 == 和 !=
SELECT Title, ISBN, Price FROM Books WHERE Price <> 65.00
4.WHERE语句也可以用LIKE操作来实现模式匹配,但这里的通配符不是通常使用的‘*’和‘?’,而是用‘%’表示多个或单个字符,用‘_’表示单个字符,以下这条语句排除了Title里带‘linux’和‘unix’的图书
SELECT Title, ISBN, Price FROM Books WHERE Title Not LIKE '%n_x%'
返回所有带单引号的书名,字符串中的单引号需要用2个单引号表示
SELECT Title FROM Books WHERE Title LIKE '%''%'
只找出那些图书与出版社相匹配的数据
SELECT Title FROM Books WHERE Books.Publisher_Id = Publishers.Publisher_Id
也可以使用SQL来改变数据库中的数据EE
1.将书名中包含“C++”的图书降价5美元
UPDATE Books SET Price = Price - 5.00 WHERE Title LIKE '%C++%'
2.删除所有C++图书
DELETE FROM Books WHERE Title LIKE '%C++%'
3.在表中插入数据
INSERT INTO Books VALUES ('A Guide to the SQL Standard', '0-201-96426-0', '0201', '47.95')
4.当然,在增删改查之前,我们需要创建表
CREATE TABLE Books
(
Title CHAR(60),
ISBN CHAR(13),
Publisher_Id CHAR(6),
Price DECIMAL(10, 2)
)
数据类型 | 说明 |
---|---|
CHARACTER(N)或CHAR(n) | 固定长度的字符串 |
NUMERIC(m, n), DECIMAL(m, n) or DEC(m, n) | m位长度的十进制数,其中小数点后位n位 |
JDBC配置
1.安装MYSQL数据库
官网(https://dev.mysql.com/downloads/)
下载免安装版,直接解压,将bin目录加到系统路径下;
可以解压在D:\mysql-8.0.16-winx64
添加系统路径D:\mysql-8.0.16-winx64\bin
在D:\mysql-8.0.16-winx64中新建my.ini
[mysql]
default-character-set=utf8
[mysqld]
port=3306
basedir=D:\mysql-8.0.16-winx64
datadir=D:\mysql-8.0.16-winx64\data
max_connections=200
character-set-server=utf8
default-storage-engine=INNODB
在bin路径下打开cmd输入命令 mysqld --initialize-insecure ,可以发现自动创建了一个data文件夹
继续输入命令 mysqld -install 安装数据库
继续输入命令 net start server 启动数据库
输入命令 mysql -u root -p 登陆;
输入命令CREATE DATABASE wangxianlu;创建名为wangxianlu的数据库;
2.数据库URL
在连接数据库之前,必须弄清楚各种与数据库类型相关的参数,例如主机名、端口号和数据库名。
JDBC使用了一种与普通URL类似的语法来描述数据源,以下是2个实例
jdbc:mysql://localhost:3306/wangxianlu;create=true
jdbc:mysql:wangxianlu
3.驱动程序jar文件
下载mysql-connector-java-5.1.44-bin.jar包
把该jar包放在工程的lib目录下
添加到程序的libraries里
4.连接数据库
可以在java代码中打开一个数据库并操作它
//建立一个指定数据库的连接,并返回一个Connection对象
static Connectin getConnection(String url, String user, String password)
具体请参见如下代码及注释。
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDB {
//数据库url
static String url = "jdbc:mysql://localhost:3306/wangxianlu?useSSL=false";
//用户名
static String username = "root";
//密码
static String password = "root";
public static void main(String[] args) throws IOException {
try{
runTest();
}catch(SQLException e){
for(Throwable t : e){
t.printStackTrace();
}
}
}
public static void runTest() throws SQLException, IOException{
try(
//连接数据库
Connection conn = DriverManager.getConnection(url, username, password);
Statement stat = conn.createStatement();){
stat.executeUpdate("CREATE TABLE Greetings (Message CHAR(20))");
stat.executeUpdate("INSERT INTO Greetings values ('Hello, World!')");
try(ResultSet result = stat.executeQuery("SELECT * FROM Greetings")){
if(result.next()){
System.out.println(result.getString(1));
}
stat.executeUpdate("DROP TABLE Greetings");
}
}
}
}
使用JDBC语句
在执行SQL语句之前,首先需要创建一个Statement对象,要创建Statement对象,首先要调用DriverManager.getConnection方法与数据库进行连接
Connection conn = DriverManeger.getConection(url, username, password);
Statement stat = conn.createStatement();
接着,把要执行的SQL语句放入到字符串中,例如:
String command = "UPDATE Books SET Price = Price - 5.00 WHERE Title NOT LIKE '%Introduction%'";
然后,使用Statement接口中的executeUpdate方法:
stat.executeUpdate(command);
executeUpdate方法返回受SQL语句影响的行数,对于不返回行数的语句返回0。
executeUpdate方法可以用在诸如DELETE,INSERT,UPDATE之类的操作,也可以执行诸如 CREATE TABLE 和 DROP TABLE 之类的数据定义语句。但是,执行查询语句时,必须使用executeQuery方法。
另外还有一个execute方法可以执行任意的SQL语句,此方法通常只用于由用户提交的交互式查询。我的理解是最好是不使用用execute方法,除非是在不确定执行操作的时候才用execute方法(例如直接让用户输入命令)。
当我们执行查询语句时,最关心的是查询结果,executeQuery方法会返回一个ResultSet类型的对象,可以通过它每次一行的迭代遍历所有查询结果
ResultSet rs = stat.executeQuery("SELECT * FROM Books");
while(rs.next()){
//look at a row of the result set
}
以下列举了Connection, Statement,和ResultSet中的方法
java.sql.Connection
//创建一个Statement对象,可以执行不带参数的SQL查询和更新
Statement createStatement()
//立即关闭当前连接,并释放由它创建的JDBC资源
void close
java.sql.Statement
//执行给定字符串中的SQL语句,并返回一个用于查看查询结果的resultSet对象
ResultSet executeQuery(String sqlQuery)
//执行给定字符串中的SQL语句,并返回受影响的行数
int executeUpdate(String sqlStatement);
//执行字符串中给定的INSERT,UPDATE,DELETE语句,还可以执行数据定义语言(DDL)的语句,如CREATE TABLE,返回受影响的行数,如果是没有更新计数的语句,则返回0
int executeLargeUpdate(String sqlStatement);
//执行给定字符串中的SQL语句,可能会产生多个结果集和更新计数,如果第一个执行结果是结果集,则返回true,反之返回false。调用getResultSet或getUpdateCount方法可以得到第一个执行结果
boolean execute(String sqlStatement)
//返回前一条查询语句的结果集,如果前一条语句未产生结果集,则返回null,对于每一条执行过的语句,该方法只能被调用一次
ResultSet getResultSet()
//返回前一条执行语句的受影响行数
int getUpdateCount()
//返回前一条执行语句的受影响行数,如果前一条语句未产生结果集,则返回-1,对于每一条执行过的语句,该方法只能被调用一次
long getLargeUpdateCount()
//关闭Statement对象以及它所对应的结果集
void close()
//语句如果被关闭则返回true
boolean isClosed()
//使得一旦所有的语句被关闭,则关闭该语句
void closeOnCompletion()
java.sql.ResultSet
//将结果集中的当前行向前移动一行。如果已经到达最后一行的后面,则返回false。注意:初始情况下必须调用该方法才能转到第一行
boolean next()
//Xxx指数据类型,如int, double String Date
Xxx getXxx(int columnNumber)
//Xxx指数据类型,如int, double String Date
Xxx getXxx(String columnLabel)
//对于给定的列序号或列标签返回或更新该列的值,并将值转换为指定的类型
<T> T getObject(int columnIndex, Object x, SQLType targetSqlType)
<T> T getObject(String columnLabel, Object x, SQLType targetSqlType)
//用给定的列名,返回该列的序号
int findColumn(String columnName)
//关闭当前结果集
void cloce()
//如果该语句被关闭,则返回true
boolean isClosed()
使用完ResultSet,Statement或Connection对象时,应立即调用close()方法。这些对象都使用了规模较大的数据结构,它们会占用数据库存服务器上的有限资源。
组装数据库
import java.io.IOException;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
public class TestDB {
static String url = "jdbc:mysql://localhost:3306/wangxianlu?useSSL=false";
static String username = "root";
static String password = "root";
public static void main(String[] args) throws IOException{
try(Scanner in = args.length == 0 ? new Scanner(System.in) :
new Scanner(Paths.get(args[0]), "utf-8")){
try(Connection conn = DriverManager.getConnection(url, username, password);
Statement stat = conn.createStatement();){
while(true){
if(args.length == 0){
System.out.println("ENTER COMMAND OR EXIT TO EXIT: ");
}
if(!in.hasNextLine())return;
String line = in.nextLine().trim();
if(line.equalsIgnoreCase("EXIT"))return;
if(line.endsWith(";")){
line = line.substring(0, line.length() - 1);
}
try{
boolean isResult = stat.execute(line);
if(isResult){
try(ResultSet rs = stat.getResultSet()){
showResultSet(rs);
}
}else{
int updateCount = stat.getUpdateCount();
System.out.println(updateCount + "rows updated");
}
}catch(SQLException ex){
for(Throwable e : ex){
e.printStackTrace();
}
}
}
}catch (SQLException ex){
for(Throwable t : ex){
t.printStackTrace();
}
}
}
}
public static Connection getConnection() throws SQLException, IOException{
Properties props = new Properties();
return DriverManager.getConnection(url, username, password);
}
public static void showResultSet(ResultSet result) throws SQLException{
ResultSetMetaData metaData = result.getMetaData();
int columnCount = metaData.getColumnCount();
for(int i = 1; i <= columnCount; i++){
if(i > 1) System.out.print(", ");
System.out.print(metaData.getColumnLabel(i));
}
System.out.println();
while (result.next()){
for(int i = 1; i <= columnCount; i++){
if(i > 1) System.out.print(", ");
System.out.print(result.getString(i));
}
System.out.println();
}
}
}
将下列语句一句一句执行就可以了>_<
CREATE TABLE Author (Author_ID CHAR(6), Name CHAR(30), Fname CHAR(30));
CREATE TABLE Books (Title CHAR(100), ISBN CHAR(30), Publisher_ID CHAR(6), Price DECIMAL(10, 2));
CREATE TABLE BooksAuthors (ISBN CHAR(30), Author_ID CHAR(30), Seq_No CHAR(6));
CREATE TABLE Publishers (Publisher_ID CHAR(6), Name CHAR(30), URL CHAR(80));
INSERT INTO Author VALUES ('ALEX', 'Alexander', 'Cristopher');
INSERT INTO Author VALUES ('BROO', 'Brooks', 'Frederick P.');
INSERT INTO Books VALUES ('A Guide to the SQL Standard', '0-201-96426', '0201', '47.95');
INSERT INTO Books VALUES ('APattern Language: Towns,Buildings, Construction', '0-19-501919-9', '019', '65.00');
INSERT INTO BooksAuthors VALUES ('0-201-96426-0', 'DATE', '1');
INSERT INTO BooksAuthors VALUES ('0-201-96426', 'DARW', '2');
INSERT INTO BooksAuthors VALUES ('0-19-501919-9', 'ALEX', '1');
INSERT INTO Publishers VALUES ('0201', 'Addison-Wesley', 'www.aw-bc.com');
INSERT INTO Publishers VALUES ('0407', 'John Wiley & Sons', 'www.wiley.com');
执行查询操作
除了上面提到的查询方法,在此基础上,如果存在多个不确定的查询目标,例如用户想要查询Publisher_Id为自己输入的值,此时可以使用预备语句(prepared statement)
在预备查询语句中,每个宿主变量都用’?'代替,下面我们定义一个查询语句
//定义SQL语句
String publisherQuery = "SELECT Books.Price, Books.Title" +
" FROM Books, Publishers" +
" WHERE Books.Publisher_Id" +
" = Publishers.Publisher_Id AND Publishers.Name = ?";
//创建预备语句
PreparedStatement stat = conn.prepareStatement(publisherQuery);
此时可以使用set方法将’?‘变量绑到实际的值上,1表示第一个’?’,如果有多个‘?’,可以依序使用setXxx方法赋值。如果想要重用预备查询语句,除非使用set方法重新赋值,或者是使用clearParameters方法,否则所有变量的绑定都不会改变。
stat.setString(1, "Addison-Wesley");
在所有变量都绑定了具体的值以后,就可以执行查询操作了
ResultSet rs = stat.executeQuery();
如果UPDATE语句需要使用出版社代码,而我们只知道出版社名称,则可以使用嵌套语句来解决
String command = "UPDATE Books SET Price =" +
"Price + ? WHERE Books.Publisher_Id =" +
" (SELECT Publisher_Id FROM Publishers WHERE Name = ?)";
以下为完整代码
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.ArrayList;
import java.util.List;
import java.util.Scanner;
public class QueryTest {
private static final String url = "jdbc:mysql://localhost:3306/wangxianlu?useSSL=false";
private static final String username = "root";
private static final String password = "root";
private static final String allQuery = "SELECT Books.Price, Books.Title" +
" FROM Books";
private static final String authorpublisherQuery = "SELECT Books.Price, " +
"Books.Title FROM Books, Author, BooksAuthors, Publishers" +
" WHERE Author.Author_Id = BooksAuthor.Author_Id" +
" AND BooksAuthors.ISBN = Books.ISBN" +
" Books.Publisher_Id = Publishers.Publisher_Id" +
" AND Authors.Name = ?" +
" AND Publisher.Name = ?";
private static final String authorQuery = "SELECT Books.Price, Books.Title" +
"FROM Books, Author, BooksAuthors" +
" WHERE Author.Author_Id = BooksAuthor.Author_Id" +
" AND BooksAuthors.ISBN = Books.ISBN" +
" AND Authors.Name = ?";
private static final String publisherQuery = "SELECT Books.Price, Books.Title" +
" FROM Books, Publishers" +
" WHERE Books.Publisher_Id = Publishers.Publisher_Id" +
" AND Publisher.Name = ?";
private static final String priceUpdate = "UPDATE Books SET Price = Price + ?" +
"WHERE Books.Publisher_Id = (SELECT Publisher_Id FROM Publishers WHERE Name = ?)";
private static Scanner in;
private static ArrayList<String> authors = new ArrayList<>();
private static ArrayList<String> publishers = new ArrayList<>();
public static void main(String[] args) throws SQLException {
//连接数据库
try (Connection conn = DriverManager.getConnection(url, username, password)) {
//赋值给用户输入in
in = new Scanner(System.in);
//赋初始值
authors.add("Any");
publishers.add("Any");
//创建Statement对象
try (Statement stat = conn.createStatement()) {
String query = "SELECT Name FROM Author";
//遍历Author表
try (ResultSet rs = stat.executeQuery(query)) {
while (rs.next()) {
//Author表内的值赋给authors
authors.add(rs.getString(1));
}
}
query = "SELECT Name FROM Publishers";
//遍历Publishers表
try (ResultSet rs = stat.executeQuery(query)) {
while (rs.next()) {
//Publishers表内的值赋给publishers
publishers.add(rs.getString(1));
}
}
}
boolean done = false;
while(! done){
//提示用户输入q,c,或e
System.out.print("Q)uery C)hange prices E)xit: ");
String input = in.next().toUpperCase();
//用户输入q,则执行查询函数
if(input.equals("Q")) executeQuery(conn);
//用户输入c,则执行更新函数
if(input.equals("C")) changePrices(conn);
//用户输入其他,则程序执行完毕
else done = true;
}
}
}
//查询函数
private static void executeQuery(Connection conn) throws SQLException{
//选择函数,获取用户二次输入
String author = select("Authors: ", authors);
String publisher = select("Publishers: ", publishers);
PreparedStatement stat;
if(! author.equals("Any") && publisher.equals("Any")) {
stat = conn.prepareStatement(authorpublisherQuery);
stat.setString(1, author);
stat.setString(2, publisher);
}else if (!author.equals("Any") && publisher.equals("Any")){
stat = conn.prepareStatement(authorQuery);
stat.setString(1, author);
}else if (author.equals("Any") && !publisher.equals("Any")){
stat = conn.prepareStatement(publisherQuery);
stat.setString(1, publisher);
}else stat = conn.prepareStatement(allQuery);
try(ResultSet rs = stat.executeQuery()){
while (rs.next()) {
System.out.println(rs.getString(1) + ", " + rs.getString(2));
}
}
}
//更新函数
private static void changePrices(Connection conn) throws SQLException{
//选择函数,获取用户二次输入
String publisher = select("Publishers: ", publishers.subList(1, publishers.size()));
System.out.print("Change prices by: ");
//获取用户第三次输入,获取用户想要涨价的值
double priceChange = in.nextDouble();
PreparedStatement stat = conn.prepareStatement(priceUpdate);
stat.setDouble(1, priceChange);
stat.setString(2, publisher);
int r = stat.executeUpdate();
System.out.println(r + "records updated.");
}
//选择函数
public static String select(String prompt, List<String> options) {
while (true) {
//提示用户输入哪个表的信息
System.out.println("请选择要查询信息的数字代号");
System.out.println(prompt);
for(int i = 0; i < options.size(); i++)
System.out.printf("%2d) %s%n", i + 1, options.get(i));
//获取用户需要的表信息
int sel = in.nextInt();
if(sel > 0 && sel <= options.size())
return options.get(sel - 1);
}
}
}
执行结果
这个实在是太多了,分2节写吧>_<