JDBC(Java DataBase Connectivity)
作用:是Java与数据库之间的桥梁。可为多种关系型DBMS提供统一的访问方式。
JDBC简单架构:
JDBC基本流程:
- 与数据库建立连接。
- 使用sql进行CUDR(增删改查)。
- 返回处理结果。
初始化驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
连接数据库
使用DriverManager.getConnection(URL,userName,userPwd)获取连接。
url是数据库连接字符串。“jdbc:mysql://127.0.0.1:3306/数据库名”
userName是用户名,也就是数据库用户的用户名。(根用户名为root)
userPwd是用户密码。
try{
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/school", "root", "123456");
}catch (SQLException e) {
e.printStackTrace();
}
CUDR
增删改
Connection connection = null;
Statement stmt = null;
try{
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/school", "root", "123456");
stmt = connection.getStatement();
String sql = "insert into/delete from/update set......";
stmt.execute(sql);
//或者:int n = stmt.executeUpdate(sql),n为成功执行的数据元组数。
}catch (SQLException e) {
e.printStackTrace();
}finally{
stmt.close();
connection.close();
}
使用statement可以进行sql的执行,但是使用statement会导致一个安全问题:sql注入。也就是在查询时写上用户名为322OR1=1–,则1=1永真并且–屏蔽掉后面的代码了,这就导致了用户名密码永远正确。
为了解决这个问题,使用预编译statement(PrepareStatement)
Connection connection = null;
PrepareStatement pstmt = null;
try{
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/school", "root", "123456");
pstmt = connection.getPrepareStatement();
String sql = "insert into/delete from/update set...?, ?, ?";
pstmt.setInt(1, ...);
pstmt.setInt(2, ...);
pstmt.setString(3, ...);
pstmt.execute(sql);
//或者:int n = pstmt.executeUpdate(sql),n为成功执行的数据元组数。
}catch (SQLException e) {
e.printStackTrace();
}finally{
pstmt.close();
connection.close();
}
注意:setInt的下标时从1开始的。下标也可以使用数据库表中字段。
查
查询操作与增删改不同。查询会返回一个结果,结果中可能包含多条查询数据。
Connection connection = null;
PrepareStatement pstmt = null;
Result rs = null;
try{
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/school", "root", "123456");
pstmt = connection.getPrepareStatement();
String sql = "select * from student";
rs = pstmt.execute(sql);
//rs是一个迭代器。
while(rs.next()){
int a = rs.getInt(1);
int b = rs.getInt(2);
String c = rs.getString(3);
}
}catch (SQLException e) {
e.printStackTrace();
}finally{
rs.close();
pstmt.close();
connection.close();
}
在使用rs的时候不能使用数据库表的字段获取值。并且要注意rs、pstmt、connection需要关闭。
使用自动关闭的rs、pstmt、connection
将这些对象写在try的括号中。
try(Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/school", "root", "123456");
PrepareStatement pstmt = connection.getPrepareStatement();
Result rs;
){
String sql = "select * from student";
rs = pstmt.execute(sql);
//rs是一个迭代器。
while(rs.next()){
int a = rs.getInt(1);
int b = rs.getInt(2);
String c = rs.getString(3);
}
}catch (SQLException e) {
e.printStackTrace();
}
JDBC处理大文本及二进制类型数据
varchar最多只能存放4000byte个数据,如果你想存放一本小说那么这是远远不够的。而text类型可以解决这个问题。
处理稍大型数据的方法:
-
使用存放路径:
通过使用JDBC存储文件路径(文件存放在服务器),读取时通过IO操作进行读取。
优点:轻便。
缺点:严格按照路径进行存取,不易移植。
-
使用数据库提供的text与blob:
直接将文件数据存放在数据库中。
优点:直接存放在数据库中,不受路径限制。
缺点:笨重。
存放小说数据:
public class JDBCSaveText {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "insert into book values (?, ?, ?)";
try (Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user_school?characterEncoding=UTF-8", "root", "111111");
PreparedStatement pstmt = connection.prepareStatement(sql)){
File file = new File("f:/逆袭1.txt");
InputStream in = new FileInputStream(file);
Reader reader = new InputStreamReader(in, "utf-8");
pstmt.setString(1, "逆袭");
pstmt.setInt(2, 11);
pstmt.setCharacterStream(3, reader, (int)file.length());
pstmt.execute();
}catch (SQLException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在存放数据时,以输入流的方式存放在数据库中。这时存放在数据库中的数据使用SQLyog可以看到数据(其他可视化软件可能看不到)。
对文件流对象进行重新编码使用FileReader是不行的,只能使用InputStreamReader进行重新编码。
使用缓冲区读取小说数据:
public class JDBCSaveText {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "select content from book where bookName = ?";
try (Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user_school?characterEncoding=UTF-8", "root", "111111");
PreparedStatement pstmt = connection.prepareStatement(sql)){
char[] data = new char[100];
//data作为缓冲区
pstmt.setString(1, "逆袭");
ResultSet rs = pstmt.executeQuery();
if (rs.next()){
Reader reader = rs.getCharacterStream(1);
int i = -1;
while((i = reader.read(data)) != -1){
System.out.println(data);
//将缓冲区中的数据输出
}
}
}catch (SQLException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用缓冲区读取:
使用reader.read(data)会将数据读入data并返回一个数(读取了的长度),输入流reader中没有数据后再使用read()会返回一个-1。
二进制与大文本处理方法一样,只不过将set/getCharacterStream换成set/getBinaryStream即可进行。
取出二进制数据:
public class JDBCGetBinary {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String sql = "select imgdata from image where imgName = ?";
try (Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user_school?characterEncoding=UTF-8", "root", "111111");
PreparedStatement pstmt = connection.prepareStatement(sql)){
byte[] data = new byte[100];
//data作为缓冲区
pstmt.setString(1, "头像.jpg");
ResultSet rs = pstmt.executeQuery();
if (rs.next()){
//设置文件路径
File file = new File("f:/头像.jpg");
FileOutputStream out = new FileOutputStream(file);
InputStream reader = rs.getBinaryStream(1);
int i = -1;
while((i = reader.read(data)) != -1){
out.write(data, 0, i);
}
}
}catch (SQLException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}