数据库管理系统负责对数据进行管理、维护和使用。现在主流数据库管理系统有Oracle、SQL Server、DB 2、Sysbase和MySQL等。
MySQL是流行的开放源码SQL数据库管理系统,由MySQL AB公司开发,先被Sun公司收购,后来又被Oracle公司收购,现在MySQL数据库是Oracle旗下的数据库产品,Oracle负责提供技术支持和维护。
目前Oracle提供了多个MySQL版本,其中社区版MySQL Community Edition是免费的,社区版本比较适合中小企业数据库。
MySQL可在Windows、Linux和UNIX等操作系统上安装和运行。这里选择的是Windows版中的mysql-installer-community-5.7.27.0.msi安装文件。
双击运行
安装类型:Developer Default(开发者安装)、Server only(只安装服务器)、Client only(只安装客户端)、Full(全部安装)和Custom(自定义安装)。对于学习和开发可以选择Developer Default 安装。
再往下,根据提示,缺啥安啥… 然后就到下面了,默认第一个选项,再next
Config Type下拉列表可以选择配置类型:
Development Computer(开发):该选项代表典型个人用桌面工作站,假定机器上运行着多个桌面应用程序。将MySQL服务器配置成使用最少的系统资源。
Server Computer(服务器):该选项代表服务器,MySQL服务器可以同其他应用程序一起 运行,例如FTP、email和web服务器。MySQL服务器配置成使用适当比例的系统资源。
Dedicated Computer(专用MySQL服务器):该选项代表只运行MySQL服务的服务器。假定没有运行其它应用程序。MySQL服务器配置成使用所有可用系统资源。
各取所需,其他选项默认就好,然后next,
自行设置密码(要求4位以上),还可以Add User添加其他账号…next…execute
点击windows图标(我装在了win10上),最近添加
这个工具就是MySQL命令行客户端工具,可以使用MySQL命令行客户端工具连接到MySQL服务器,
要求输入root密码(刚刚设的)按Enter键,如果密码正确则连接到MySQL服务器。
快速连接服务器方式连接的是本地数据库,如果服务器不在本地,而是在一个远程主机上,需要使用通用的连接方式:
在操作系统下打开一个终端窗口,Windows下是命令行工具,再次输入mysql -h localhost -u root –p命令。如图28-10所示,如果出现“‘MySQL’ 不是内部或外部命令,也不是可运行的程序或批处理文件。” 的错误,则说明在环境变量的Path没有配置MySQL的Path,追加 C:\Program Files\MySQL\MySQL Server 5.7\bin到环境变量Path之后。
mysql -h localhost -u root -p命令参数说明:
-h:要连接的服务器主机名或IP地址,可以是远程的一个服务器主机,也可以是-hlocalhost 方式,注意没有空格。
-u:是服务器要验证的用户名,这个用户一定是数据库中存在的,并且具有连接服务器的权限,也可以是-uroot方式,注意没有空格。
-p:是与上面用户对应的密码,也可以直接输入密码-p123456,123456是root密码(自己设的)。
所以mysql -h localhost -u root -p命令也可以替换为mysql -hlocalhost -uroot -p123456。
通过命令行客户端管理MySQL数据库
help
列出MySQL其他命令的帮助,在命令行客户端中输入help,不需要分号结尾,直接按下Enter键。
(这里都是MySQL的管理命令,这些命令大部分不需要分号结尾)
退出命令
quit或exit,不需要分号结尾。
数据库管理
在使用数据库的过程中,有时需要知道服务器中哪些数据库或自己创建和删除数据库。
查看数据库show databases;,注意有分号结尾。
创建数据库create database testdb;,testdb是自定义数据库名,注意有分号结尾。
删除数据库database testdb;,注意有分号结尾。
数据表管理
在使用数据库的过程中,有时需要知道某个数据库下有多少个数据表,并想查看表结构等信息。
查看有多少个数据表show tables;,有分号结尾。因为一个服务器中有很多数据库,应该先使用use选择数据库,use world结尾没有分号。如果没有选择数据库,会发生错误。
知道了有哪些表后,还需要知道表结构,使用desc命令,想知道city表结构可以使用命令desc city;,有分号结尾。
Java中数据库编程是通过JDBC(Java Database Connectivity)实现的:
Java官方提供JDBC接口,如Connection、Statement和ResultSet等。
数据库厂商为了支持Java语言使用自己的数据库,他们根据这些接口提供了具体的实现类(称为JDBC Driver(JDBC驱动程序)),例如Connection是数据库连接接口,如何能够高效地连接数据库或许只有数据库厂商自己清楚,因此他们提供的JDBC驱动程序当然是最高效的,针对某种数据库也可能有其他第三方JDBC驱动程序。
对于开发人员而言,JDBC提供了一致的API,开发人员不用关心实现接口的细节。
JDBC API为Java开发者使用数据库提供了统一的编程接口,它由一组Java类和接口组成。这种类和接口来自于java.sql和javax.sql两个包:
java.sql 其中的类和接口主要针对基本的数据库编程服务,如创建连接、执行语句、语句预编译和批处理查询等。同时也有一些高级的处理,如批处理更新、事务隔离和可滚动结果集等。
javax.sql 主要为数据库方面的高级操作提供了接口和类,提供分布式事务、连接池和行集等。
在编程实现数据库连接时,JVM必须先加载特定厂商提供的数据库驱动程序。使用Class.forName()方法实现驱动程序加载过程:
不同驱动程序的装载方法:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // JDBC-ODBC桥接,Java自带
Class.forName("特定的JDBC驱动程序类名"); // 数据库厂商提供
// 加载MySQL驱动
Class.forName("com.mysql.jdbc.Driver");
如果直接这样运行程序会抛出ClassNotFoundException异常:
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
因为程序无法找到MySQL驱动程序com.mysql.jdbc.Driver类,需要配置当前项目的类路径 (Classpath),在类路径中包含MySQL驱动程序。
我在安装MySQL的目录里没看到驱动,就自己去下了一个,网址
然后我放在了 C:\Program Files\MySQL\mysql-connector-java-5.1.9.jar
(一般在发布java文件时,会把字节码文件即class文件打包成.jar文件,.jar文件是一种基于.zip结构的压缩文件)
Eclipse中将.jar文件添加到项目步骤:
选择项目,右击菜单中选择“构建路径”→“配置构建路径”,在弹出对话框中选择“Java构建路径”→“库”,其中“添加JAR”是在当前项目中找.jar文件(推荐,当然得把.jar文件考进项目里面),“添加外部JAR”是在项目之外找.jar文件。
然后就可以愉快的运行了。
可以通过调用DriverManager类的getConnection()方法实现:
上面的几个getConnection()方法都会抛出受检查的SQLException异常,注意处理这个异常。
JDBC的URL类似于其他场合的URL,它的语法如下:
jdbc:
这里有三个部分,它们用冒号隔离。
对于不同的数据库,厂商提供的驱动程序和连接的URL都不同:
数据库名 | 驱动程序 | URL |
---|---|---|
MS SQLServer | com.microsoft.jdbc.sqlserver.SQLServerDriver | jdbc:microsoft://[ip]:[port]; user=[user]:password=[password] |
JDBC-ODBC | sun.jdbc.odbc.JdbcOdbcDriver | jdbc:odbc:[odbcsource] |
Oracle thin Driver | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@[ip]:[port]:[sid] |
MySQL | com.mysql.jdbc.Driver | jdbc:mysql://ip/database |
建立数据连接:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class HelloWorld {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("驱动程序加载成功...");
} catch (ClassNotFoundException e) {
System.out.println("驱动程序加载失败...");
// 退出
return;
}
String url = "jdbc:mysql://localhost:80/MySQL"; // 本地数据库
String user = "root";
String password = "123456"; // 自己设的密码
try (Connection conn = DriverManager.getConnection(url, user, password)) {
System.out.println("数据库连接成功:" + conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
第22行使用DriverManager的getConnection(String url, String user, String password)方法建立数据连接,在url中3306是数据库端口号,MyDB是MySQL服务器中的数据库。
外Connection接对象是通过自动资源管理技术释放资源的。
Connection对象代表的数据连接不能被JVM的垃圾收集器回收,在使用完连接后必须关闭 (调用close()方法),否则连接会保持一段比较长的时间,直到超时。Java7之前都在finally模块中关闭数据库连接。Java 7之后Connection接口继承了AutoCloseable接口,可以通过自动资源管理技术释放资源。
上面代码虽然可以成功建立连接,但是控制台会有警告。
由于现在的网络通信为了提高网络完全,都要求使用SSL (Secure Sockets Layer 安全套接层) 安全协议。但是由于各种原因,很多服务器并未使用SSL安全协议,特别是对于学习和测试阶段可以不使用SSL安全协议。为此,需要修改url连接字符串:“jdbc:mysql://localhost:3306/MyDB?verifyServerCertificate=false&useSSL=false”
verifyServerCertificate设置为false表示不进行安全认证,useSSL设置为false表示不使用SSL进行网络通信。
(SSL为网络通信提供安全及数据完整性的一种安全协议,在传输层对网络连接进行加密。)
数据连接的url字符串可以有很多参数对,包括数据库用户名和密码也都可以参数对形式放到url字符串 中,有的url字符串会很长维护起来不方便,可以把这些参数对放置到Properties对象中:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class HelloWorldWithProp {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("驱动程序加载成功...");
} catch (ClassNotFoundException e) {
System.out.println("驱动程序加载失败...");
// 退出
return;
}
String url = "jdbc:mysql://localhost:80/MySQL";
// 创建Properties对象
Properties info = new Properties();
// 设置参数 setProperty()方法键和值都是字符串类型
info.setProperty("user", "root");
info.setProperty("password", "123456"); // 自己设的密码
info.setProperty("verifyServerCertificate", "false");
info.setProperty("useSSL", "false");
try (Connection conn = DriverManager.getConnection(url, info)) {
System.out.println("数据库连接成功:" + conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
但是这些参数都是“硬编码 ”在程序代码中的,程序编译之后不能修改。但是数据库用户名、密码、服务器主机名、端口等等这一切,在开发阶段和部署阶段可能完全不同,这些参数信息应该可以配置的,可以放到一个属性文件中,借助于输入流,可以在运行时读取属性文件内容到Properties对象中。
(硬编码俗称“写死”,指将可变变量用一个固定值来代替的方法,用这种方法编译后,如果以后需要更改此变量就非常困难了。)
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class HelloWorldWithProFile {
public static void main(String[] args) {
// 加载驱动程序
Properties info = new Properties();
try {
// 获得config.properties属性文件输入流对象
InputStream input = HelloWorldWithProFile.class.getClassLoader()
.getResourceAsStream("config.properties");
// 从流中加载信息到Properties对象中
info.load(input);
} catch (IOException e) {
// 退出
return;
}
String url = "jdbc:mysql://localhost:80/MySQL";
try (Connection conn = DriverManager.getConnection(url, info)) {
System.out.println("数据库连接成功:" + conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
第19行,属性文件放在src目录(注意不是和当前调用的在一起,亲测)下,但是编译时,这些文件会被复制到字节码文件所在的目录中,这种目录成为资源目录,获得资源目录要通过Java反射机制, HelloWorldWithPropFile.class.getClassLoader().getResourceAsStream(“config.properties”)语句能够获得运行时config.properties的文件输入流对象。
config.properties文件内容:
user=root
password=123456
useSSL=false
verifyServerCertificate=false
java.sql.Connection接口的实现对象代表与数据库的连接,也就是在Java程序和数据库之间建立连接。
常用方法:
Statement createStatement() 创建一个语句对象,语句对象用来将SQL语句发送到数据库。
PreparedStatement prepareStatement(String sql) 创建一个预编译的语句对象,用来将参数化的SQL语句发送到数据库,参数包含一个或者多个问号“?”占位符。
CallableStatement prepareCall(String sql) 创建一个调用存储过程的语句对象,参数是调用的存储过程,参数包含一个或者多个问号“?”为占位符。
close() 关闭到数据库的连接,在使用完连接后必须关闭,否则连接会保持一段比较长的时间,直到超时。
isClosed() 判断连接是否已经关闭。
java.sql.Statement称为语句对象,它提供用于向数据库发出SQL语句,并且访问结果。Connection接口提供了生成Statement的方法,一般情况下可以通过connection.createStatement()方法就可以得到Statement对象。
有三种Statement接口:java.sql.Statement、java.sql.PreparedStatement和 java.sql.CallableStatement。
其中PreparedStatement继承Statement接口,CallableStatement继承PreparedStatement接口。
Statement实现对象用于执行基本的SQL语句,PreparedStatement实现对象用于执行预编译的SQL语句,CallableStatement实现对象用于用来调用数据库中的存储过程。
预编译SQL语句是在程序编译时一起进行编译,这样的语句在数据库中执行时候,不需要编译过程,直接执行SQL语句,所以速度很快。在预编译SQL语句会有一些程序执行时才能确定的参数,这些参数采用“?”占位符,直到运行时再用实际参数替换。
常用方法:
executeQuery() 运行查询语句,返回ResultSet对象。
executeUpdate() 运行更新操作,返回更新的行数。
close() 关闭语句对象。
isClosed() 判断语句对象是否已经关闭。
Statement对象用于执行不带参数的简单SQL语句,它的典型使用如下:
Connection conn = DriverManager.getConnection("jdbc:odbc:accessdb","admin","admin");
Statement stmt = conn.createStatement();
ResultSet rst = stmt.executeQuery("select userid, name from user");
PreparedStatement对象用于执行带参数的预编译SQL语句,它的典型使用如下:
Connection conn = DriverManager.getConnection("jdbc:odbc:accessdb","admin","admin");
PreparedStatement pstmt = conn.prepareStatement("insert into user values(?,?)"); pstmt.setInt(1,10); // 绑定第一个参数
pstmt.setString(2,"guan"); // 绑定第二个参数
pstmt.executeUpdate(); // 执行SQL语句
第2行SQL语句"insert into user values(?,?)"在Java源程序编译时一起编译,两个问号占位符所代表的参数,在运行时绑定。
绑定参数时需要注意两个问题:绑定参数顺序和绑定参数的类型,绑定参数索引是从1开始的(不是0)。根据绑定参数的类型不同选择对应的set方法。
CallableStatement对象用于执行对数据库已存储过程的调用,它的典型使用如下:
Connection conn = DriverManager.getConnection("jdbc:odbc:accessdb","admin","admin");
strSQL = "{call proc_userinfo(?,?)}";
java.sql.CallableStatement sqlStmt = conn.prepaleCall(strSQL);
sqlStmt.setString(1,"tony");
sqIStmt.setString(2,"tom"); // 执行存储过程
int i = sqlStmt.exeCuteUpdate();
在Statement执行SQL语句时,如果是SELET语句会返回结果集,结果集通过接口 java.sql.ResultSet描述,它提供了逐行访问结果集的方法,通过该方法能够访问结果集中不同字段的内容。
常用方法:
close() 关闭结果集对象。
isClosed() 判断结果集对象是否已经关闭。
next() 将结果集的光标从当前位置向后移一行。
getString() 获得在数据库里是CHAR或VARCHAR等字符串类型的数据,返回值类型是 String。
getFloat() 获得在数据库里是浮点类型的数据,返回值类型是float。
getDouble() 获得在数据库里是浮点类型的数据,返回值类型是double。
getDate() 获得在数据库里是日期类型的数据,返回值类型是java.sql.Date。
getBoolean() 获得在数据库里是布尔数据的类型,返回值类型是boolean。
getBlob() 获得在数据库里是Blob(二进制大型对象)类型的数据,返回值类型是Blob类型。
getClob() 获得在数据库里是Clob(字符串大型对象)类型的数据,返回值类型是Clob。
这些方法要求有列名或者列索引,如getString()方法的两种情况:
public String getString(int columnlndex) throws SQLException
public String getString(String columnName) throws SQLException
方法getXXX提供了获取当前行中某列值的途径,在每一行内,可按任何次序获取列值。使用列索引有时会比较麻烦,这个顺序是select语句中的顺序:
select * from user
select userid, name from user
select name,userid from user
columnlndex列索引是从1开始的(不是0)。这个顺序与select语句有关,如果select使用*返回所有字段,如select * from user语句,那么列索引是数据表中字段的顺序;如果select指定具体字段,如select userid, name from user或select name,userid from user, 那么列索引是select指定字段的顺序。
ResultSet示例代码:
String url = "jdbc:mysql://localhost:80/MySQL";
try (
// 自动资源管理技术释放资源
Connection conn = DriverManager.getConnection(url, info);
Statement stmt = conn.createStatement();
ResultSet rst = stmt.executeQuery("select name,userid from user")) {
while (rst.next()) {
System.out.printf("name:%s id:%d\n", rst.getString("name"), rst.getInt(2));
}
} catch (SQLException e) {
e.printStackTrace();
}
在遍历结果集时使用了rst.next()方法,next()是将结果集光标从当前位置向后移一行,结果集光标最初位于第一行之前;第一次调用next方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推。如果新的当前行有效,则返回true;如果不存在下一行,则返回false。
Connection对象、Statement对象和ResultSet对象都不能被JVM的垃圾收集器回收,在使用完后都必须关闭(调用它们的close()方法)。Java 7之前都在finally模块中关闭释放资源。Java 7之后它们都继承了AutoCloseable接口,可以通过自动资源管理技术释放资源。
对数据库表中数据可以进行4类操作:数据插入(Create)、数据查询(Read)、数据更新(Update) 和数据删除(Delete),即“增、删、改、查”。
通过JDBC进行数据库编程的一般过程:
其中查询(Read)过程最多需要7个步骤,修改(C插入、U 更新、D删除)过程最多需要5个步骤。这个过程采用了预编译语句对象进行数据操作,所以有可能进行绑定参数(步骤4)。
此为基本的一般步骤,实际情况会有所变化,例如没有参数需要绑定,第4步骤省略。如果Connection对象、Statement对象和ResultSet对象都采用自动资源管理技术释放资源,第7步骤省略。
User表:
字段名 | 类型 | 是否可以为Null | 主键 |
---|---|---|---|
name | varchar(20) | 是 | 否 |
userid | int | 否 | 是 |
select name,userid from user where userid > ? order by userid // 有条件查询
select max(userid) from user // 使用max等函数,无条件查询
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
public class CRUDSample {
// 连接数据库url
static String url;
// 创建Properties对象
static Properties info = new Properties();
// 1.驱动程序加载
static {
// 获得属性文件输入流
InputStream input = CRUDSample.class.getClassLoader().
getResourceAsStream("config.properties");
try {
// 加载属性文件内容到Properties对象
info.load(input);
// 从属性文件中取出url
url = info.getProperty("url");
// Class.forName("com.mysql.jdbc.Driver");
// 从属性文件中取出driver
String driverClassName = info.getProperty("driver");
Class.forName(driverClassName);
System.out.println("驱动程序加载成功...");
} catch (ClassNotFoundException e) {
System.out.println("驱动程序加载失败...");
} catch (IOException e) {
System.out.println("加载属性文件失败...");
}
}
public static void main(String[] args) {
// 查询数据
read();
}
// 数据查询操作
public static void read() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 2.创建数据库连接
conn = DriverManager.getConnection(url, info);
// 3. 创建语句对象
pstmt = conn.prepareStatement("select name,userid from " + "user where userid > ? order by userid");
// 4. 绑定参数
pstmt.setInt(1, 0);
// 5. 执行查询(R)
rs = pstmt.executeQuery();
// 6. 遍历结果集
while (rs.next()) {
System.out.printf("id: %d name: %s\n", rs.getInt(2),
rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7.释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
}
}
}
18~37行是静态代码块,在静态代码块中读取属性文件内容到Properties对象和加载驱动程序,这两个操作只需执行一次,所以最好放到静态代码块中。
本例将驱动程序类名和数据库连接的url字符串都放到属性文件中config.properties,更加方便配置,config.properties内容如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:80/MySQL
user=root
password=123456
useSSL=false
verifyServerCertificate=false
第45行read()方法是数据查询方法,查询完成之后采用finally代码块释放资源。本例也可以使用自动资源管理技术,但会引起try语句发生嵌套,反而会有些麻烦。
// 查询最大的用户Id
public static int readMaxUserId() {
int maxId = 0;
try (
// 2.创建数据库连接
Connection conn = DriverManager.getConnection(url, info);
// 3. 创建语句对象
PreparedStatement pstmt = conn.prepareStatement("select max(userid) from user");
// 4. 绑定参数
// pstmt.setInt(1, 0);
// 5. 执行查询(R)
ResultSet rs = pstmt.executeQuery()) {
// 6. 遍历结果集
if (rs.next()) {
maxId = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
}
return maxId;
}
上述代码使用了自动资源管理技术,由于没有参数需要绑定,所以ResultSet对象可以与 Connection对象和PreparedStatement对象放在一个try代码块中进行管理。而前面的有条件查询 read()方法则不行。
// 数据插入操作
public static void create() {
try ( // 2.创建数据库连接
Connection conn = DriverManager.getConnection(url, info);
// 3. 创建语句对象
PreparedStatement pstmt = conn.prepareStatement("insert into user (userid, name) values (?,?)")) {
// 查询最大值
int maxId = readMaxUserId();
// 4. 绑定参数
pstmt.setInt(1, ++maxId);
pstmt.setString(2, "Tony" + maxId);
// 5. 执行修改(C、U、D)
int affectedRows = pstmt.executeUpdate();
System.out.printf("成功插入%d条数据。\n", affectedRows);
} catch (SQLException e) {
e.printStackTrace();
}
}
第7行是创建插入语句对象,其中有两个占位符,因此需要绑定参数,第11行是绑定第一个参数,第12行是绑定第二个参数。第14行executeUpdate()方法执行SQL语句,与查询方法executeQuery()不同,executeUpdate()方法返回的是整数——成功影响的记录数, 即成功插入记录数。
// 数据更新操作
public static void update() {
try ( // 2.创建数据库连接
Connection conn = DriverManager.getConnection(url, info);
// 3. 创建语句对象
PreparedStatement pstmt = conn.prepareStatement("update user set name = ? where userid > ?")) {
// 4. 绑定参数
pstmt.setString(1, "Tom");
pstmt.setInt(2, 30);
// 5. 执行修改(C、U、D)
int affectedRows = pstmt.executeUpdate();
System.out.printf("成功更新%d条数据。\n", affectedRows);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 数据删除操作
public static void delete() {
try ( // 2.创建数据库连接
Connection conn = DriverManager.getConnection(url, info);
// 3. 创建语句对象
PreparedStatement pstmt = conn.prepareStatement("delete from user where userid = ?")) {
// 查询最大值
int maxId = readMaxUserId();
// 4. 绑定参数
pstmt.setInt(1, maxId);
// 5. 执行修改(C、U、D)
int affectedRows = pstmt.executeUpdate();
System.out.printf("成功删除%d条数据。\n", affectedRows);
} catch (SQLException e) {
e.printStackTrace();
}
}