目录
Java数据库组织架构
因为有很多中数据库,比如常见的MySQL、Oracle、SqlServer、Sqlite..... 这些数据库都有自己的特性,如果Java为每一种数据库开发一套API,进行数据库操作,那么将是很头疼的,原因如下:
1、每一种数据库都有差异,那么就代表每一套API的使用方法可能都不同;
2、不便于数据库迁移,比如,前期使用MySQL,到后期发现Oracle更加适合项目,于是将数据从MySQL迁移到Oracle中了,因为API不同,所以需要重新修改所有的SQL操作。
3、除此之外,这么多套API,维护起来也是很麻烦的;
Java采用的方式:
提供统一API定义,API的实现则由各数据库厂商自己提供(jar包,以驱动的形式),再使用的时候,加载所需要的数据库驱动即可。他的架构图如下:
这样做的好处是显而易见的,可以解决上面的问题,在使用的时候,不用可以关心底层使用什么数据库,只需要关心业务逻辑即可。
下载驱动包
下载地址:http://static.runoob.com/download/mysql-connector-java-5.1.39-bin.jar
下载之后将jar包添加到buildpath中即可。
连接数据库
连接数据库主要有三个步骤
1、加载数据库驱动类
2、设置连接数据库的相关信息
3、连接数据库
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public class ConnectDB {
public static void main(String[] args) throws ClassNotFoundException, SQLException { // 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver"); // 指定数据库连接信息
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root"; // 连接数据库
Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); // com.mysql.jdbc.JDBC4Connection@377dca04 }
}
连接数据库的高开销
其实我们连接数据库,创建Connection对象,就是创建一个和数据库服务器的Socket连接(传输层,使用TCP),通常应用程序和数据库服务器不再同一台机器上,这就涉及到了网络IO,网络IO的速度并不快。
下面测试一下建立一次连接所花费的时间:
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public class TestTimeToConnectDB {
public static void main(String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root"; long start = System.currentTimeMillis();
DriverManager.getConnection(url, user, password);
long end = System.currentTimeMillis();
System.out.println("连接本地数据库, 花费了 " + (end-start) + " 毫秒");
// 1128毫秒 1136毫秒 1193毫秒 }
}
从上面的统计结果来看(与测试环境有关),建立连接的耗时的确是挺高的,在大型应用中,这样的开销明显是不能接受的。一般的解决方法是使用连接池。
Statement接口的介绍
与数据库建立了联系之后,就可以进行数据库操作了,这是需要使用Statement接口。这个Statement接口有两个子类,分别是PreparedStatement和CallableStatement。Statement接口中定义的那些方法,在PreparedStatement和CallcableStatement类中也同样可以使用。
Statement接口主要有三个常用的方法:
// 可以用来执行所有的SQL语句,执行DML返回false,执行DQL获得结果集则返回true
boolean java.sql.Statement.execute(String sql) // 用来执行DQL语句,比如select,返回查询的结果集
ResultSet java.sql.Statement.executeQuery(String sql) // 用来执行DML语句,比如insert、update、delete,返回受影响的记录数
int java.sql.Statement.executeUpdate(String sql)
我们一般不使用Statement,而是使用它的实现类PreparedStatement。因为Statement是直接将SQL拼接之后执行,会有SQL注入的风险。而PreparedStatement可以先进行预处理,可以有效地防止SQL注入。
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement; public class UseStatement {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, user, password); // 获取Statement对象
Statement statement = connection.createStatement(); String sql = "select * from user";
boolean flag = statement.execute(sql);
System.out.println(flag); // 执行查询操作,返回true sql = "delete from user where uid=1";
flag = statement.execute(sql);
System.out.println(flag); // 执行insert、delete、update操作,返回false }
}
PreparedStatement类的使用
PrepareStatement类是Statement类的子类,可以对SQL进行预处理,并通过参数绑定的方式来防止SQL注入。同样的,PreparedStatement同样有execute()、executeQuery()、executeUpdate()三个方法。
使用PreparedStatement进行DML操作
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException; public class UsePreparedStatementRunDML {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, user, password); // 准备SQL语句,参数使用?进行占位
String sql = "insert into user (username, pwd, birthday) value (?, ?, ?)"; // 进行预处理,获得prepareStatement对象
PreparedStatement prepareStatement = connection.prepareStatement(sql); // 为sql中的占位符绑定值, setXxx(index, value); index从1开始计数
prepareStatement.setString(1, "test");
prepareStatement.setString(2, "98765");
prepareStatement.setDate(3, new java.sql.Date(System.currentTimeMillis()));
// 注意此时导入的是java.sql.Date; 这个类是java.util.Date的子类 // 使用executeUpdate()进行DML操作
int affectedRows = prepareStatement.executeUpdate();
System.out.println(affectedRows); // 1 /**
* 上面是准确知道每一个参数的类型,可以使用setXxx来绑定参数
* 如果不知道准确的参数类型,可以采取偷懒的方式,使用setObject()
*/
prepareStatement.setObject(1, "demo");
prepareStatement.setObject(2, "8888");
prepareStatement.setObject(3, new java.sql.Date(System.currentTimeMillis()));
affectedRows = prepareStatement.executeUpdate();
System.out.println(affectedRows); // 1 // 释放资源
if (prepareStatement != null) {
prepareStatement.close();
} if (connection != null) {
connection.close();
} }
}
使用PreparedStatement对象进行DQL操作
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import com.sun.corba.se.spi.orbutil.fsm.Guard.Result; /**
* 使用PreparedStatement进行查询操作
*/
public class UsePreparedStatementRunDQL { public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, user, password); // 准备SQL语句,参数使用?进行占位
String sql = "select uid, username, pwd, birthday from user where uid > ?"; // 进行预处理,获得prepareStatement对象
PreparedStatement prepareStatement = connection.prepareStatement(sql); // 绑定参数
prepareStatement.setInt(1, 3); // 使用executeQuery()执行查询DQL操作,获得的ResultSet对象相当于结果集中的游标
ResultSet resultSet = prepareStatement.executeQuery(); // 循环遍历结果集,打印所有数据
while(resultSet.next()) {
// 可以使用resultSet.getXxx(int num)来获取第num个字段的值,数据类型是Xxx
System.out.println(
resultSet.getInt(1) + "--" +
resultSet.getString(2) + "--" +
resultSet.getString(3) + "--" +
resultSet.getDate(4)
); // 也可以使用resultSet.getXxx(columnName)来获取columnName字段的值,数据类型是Xxx
System.out.println(
resultSet.getInt("uid") + "--" +
resultSet.getString("username") + "--" +
resultSet.getString("pwd") + "--" +
resultSet.getDate("birthday")
);
} // 关闭连接--->先打开的,后关闭
if (resultSet != null) {
resultSet.close();
} if (prepareStatement != null) {
prepareStatement.close();
} if (connection != null) {
connection.close();
} }
}
JDBC事务处理
事务的概念这里就不在累述了,jdbc在进行事务操作的时候,无非就是4个注意点:
1、执行SQL前,要关闭自动提交
2、执行任意一个SQL时,如果出现错误要抛出异常,捕获异常后,进行回滚操作
3、如果所有SQL都执行成功,需要进行提交,将所有操作的结果都持久化到数据库
4、最后的,也是最重要的,数据库存储引擎一定要是InnoDB,不要使用MyISAM
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException; /**
* 测试事务操作
*/
public class TransactionInJDBC {
public static void main(String[] args) { Connection connection = null;
PreparedStatement preparedStatement = null; try {
Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
connection = DriverManager.getConnection(url, user, password); connection.setAutoCommit(false); // 关闭自动提交 String sql = ""; // 进行操作 1
sql = "update user set username='God' where uid=?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 4);
preparedStatement.executeUpdate(); // 在进行下一次预处理SQL之前,要先释放一下上一次额预处理
preparedStatement.close(); // 进行操作 2 , 字段名username故意写错为usernema,所以数据库操作会失败,并抛出异常,触发回滚操作
sql = "insert into user (usernema, pwd) values (?, ?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "qwe");
preparedStatement.setString(2, "123456");
preparedStatement.executeUpdate(); // 如果到这里都没有抛出异常,那么表示所有SQL执行成功,于是可以提交操作
connection.commit(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace(); // 检测到SQLException,并且连接数据库成功,所以,肯定是执行SQL出错了,于是进行回滚操作
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally { // 释放资源
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
批量SQL操作
有时候,我们需要有很多SQL需要执行,我们可以每次执行1条,这样的效率并不怎么高,Java提供了批量执行SQL,首先需要将要执行的SQL添加到statement对象保存sql的列表中,之后调用executeBatch()执行保存的所有SQL。
批量执行SQL需要有以下几个注意点:
1、要关闭自动提交(采用事务处理)
2、对于将要执行的SQL,调用addBatch()保存
3、调用executeBatch()执行批量SQL
4、提交commit
package lixin.gan.test; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random; /**
* 使用批量的数据库操作,并测试与单一操作的效率
*/
public class UseAddBatch { private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root"; private static Connection connection = null;
private static Statement statement = null; /**
* 关闭连接,释放资源
*/
private static void freeResource(Connection connection, Statement statement) {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /**
* 批量执行SQL
*/
public static void runBatchSqlOneTime() {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(url, user, password); // 批量执行SQL之前,需要关闭自动提交,防止中途有SQL执行失败。
connection.setAutoCommit(false); // 批量执行SQL的时候,一般不会使用预处理PreparedStatement,一般是使用Statement
statement = connection.createStatement(); for (int i = 0; i < 1000; i++) {
statement.addBatch("insert into person (name, age) value ('test" + i + "', " + i%10 + ")");
}
// addBatch(sql) 会将传入的sql保存到statement对象的sql列表中 // 执行批量statement对象sql列表中的所有sql
statement.executeBatch(); connection.commit(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
// 中途出现错误
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
freeResource(connection, statement);
}
} /**
* 一次执行一次SQL
*/
public static void runSingleSqlOneTime() {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(url, user, password); statement = connection.createStatement(); for (int i = 0; i < 1000; i++) {
statement.executeUpdate("insert into person (name, age) value ('test" + i + "', " + i%10 + ")");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
freeResource(connection, statement);
}
} /**
* 一次执行一次SQL,使用事务
*/
public static void runSingleSqlOneTimeUseTransaction() {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(url, user, password);
connection.setAutoCommit(false);
statement = connection.createStatement();
for (int i = 0; i < 1000; i++) {
statement.executeUpdate("insert into person (name, age) value ('test" + i + "', " + i%10 + ")");
}
connection.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
// 中途出现错误
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
freeResource(connection, statement);
}
} public static void main(String[] args) {
long start = System.currentTimeMillis();
runBatchSqlOneTime();
long end = System.currentTimeMillis();
System.out.println("批量执行1千条SQL, 耗费了 " + (end-start) + " 毫秒"); start = System.currentTimeMillis();
runSingleSqlOneTime();
end = System.currentTimeMillis();
System.out.println("每次执行1条SQL,不使用事务,执行1千条SQL, 耗费了 " + (end-start) + " 毫秒"); start = System.currentTimeMillis();
runSingleSqlOneTimeUseTransaction();
end = System.currentTimeMillis();
System.out.println("每次执行1条SQL,使用事务,执行1千条SQl, 耗费了 " + (end-start) + " 毫秒"); /**统计结果(执行1千条SQL)
批处理 不使用事务,每次执行一条sql 使用事务,一次执行一条sql
2092 毫秒 39613 毫秒 418 毫秒
1887 毫秒 39066 毫秒 344 毫秒
1951 毫秒 32131 毫秒 339 毫秒
*/
}
}
从上面的统计结果来看,批处理 只是比 不适用事务,每次执行一条的效率 高20倍左右,但是,如果使用事务,一次执行一条,这样的效率也挺高的,比批处理的效率高5倍。可以根据具体的使用场景来选择策略
数据库存取文本内容的大对象(CLOB)
CLOB用来存储大量的文本内容,在数据库中的数据类型就是text相关的那几个:
1、tinytext 最大长度是255字节的文本内容(按照utf8编码,约86个汉字)
2、text 最大长度是65535(64K)字节的文本内容
3、mediumtext 最大长度是16777215(16M)字节的文本内容
4、longtext 最大长度是4G字节的文本内容
注意
1、上面的各种数据类型的单位都是字节
2、varchar数据类型的最大长度不能超过65535
3、Java操作CLOB是以字符流的形式(reader)
package lixin.gan.test; import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class UseClob { private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root"; private static Connection connection = null;
private static PreparedStatement preparedStatement = null;
private static ResultSet resultSet = null; private static Reader reader = null;
private static BufferedReader bufferedReader = null; /**
* 存储大文本对象数据到数据库中
*/
public static void storeClobData() {
try {
Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(url, user, password); String sql = "insert into person (name, introduction) values (?, ?)";
preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, "test"); // 对于大文本对象Clob,绑定参数使用setClob或者使用setObject()
//preparedStatement.setClob(parameterIndex, reader);
Reader reader = new InputStreamReader(new FileInputStream("data.txt"));
preparedStatement.setClob(2, reader); preparedStatement.executeUpdate(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
freeResource(connection, preparedStatement);
}
} /**
* 从数据库中取出大文本对象
*/
public static void fetchClobData() {
try {
Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(url, user, password); String sql = "select name, introduction from person where id=?";
preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, 7); resultSet = preparedStatement.executeQuery(); resultSet.next();
String name = resultSet.getString("name");
Clob introduction = resultSet.getClob("introduction"); // 获取字符流
reader = introduction.getCharacterStream();
bufferedReader = new BufferedReader(reader);
String line = "";
while((line = bufferedReader.readLine()) != null) {
System.out.println(line);
} } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
freeResource(reader, bufferedReader);
freeResource(connection, preparedStatement, resultSet);
}
} /**
* 主入口执行代码
* @param args
*/
public static void main(String[] args) {
storeClobData();
fetchClobData();
} /**
* 释放资源
* @param connection
* @param preparedStatement
*/
private static void freeResource(Connection connection, PreparedStatement preparedStatement) {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /**
* 释放资源
* @param connection
* @param preparedStatement
* @param resultSet
*/
private static void freeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /**
* 释放资源
* @param reader
* @param bufferedReader
*/
private static void freeResource(Reader reader, BufferedReader bufferedReader) {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
数据库存取二进制的大对象(BLOB)
BLOB是指大量的二进制数据(Binary Large Object),与BLOB对应的数据类型如下:
1、tinyblob 最大长度是255字节的二进制数据
2、blob 最大长度是65535(64K)字节的文本内容
3、mediumblob 最大长度是16777215(16M)字节的文本内容
4、longblob 最大长度是4G字节的文本内容
注意
1、上面的各种数据类型的单位都是字节
3、Java操作BLOB是以字节流的形式(stream)
package lixin.gan.test; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; /**
* 存取二进制数据
*/
public class UseBlob { private static String url = "jdbc:mysql://localhost:3306/test";
private static String user = "root";
private static String password = "root"; private static Connection connection = null;
private static PreparedStatement preparedStatement = null;
private static ResultSet resultSet = null; private static InputStream inputStream = null;
private static BufferedInputStream bufferedInputStream = null;
private static BufferedOutputStream bufferedOutputStream = null; /**
* 主入口执行代码
* @param args
*/
public static void main(String[] args) {
storeBlobData();
fetchBlobData();
} public static void storeBlobData() {
try {
Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(url, user, password); String sql = "insert into person (name, image) values (?, ?)";
preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, "test"); // 对于大文本对象Clob,绑定参数使用setClob或者使用setObject()
// preparedStatement.setBlob(parameterIndex, inputStream);
preparedStatement.setBlob(2, new FileInputStream("pic.png")); preparedStatement.executeUpdate(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
freeResource(connection, preparedStatement);
}
} public static void fetchBlobData() {
try {
Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(url, user, password); String sql = "select name, image from person where id=?";
preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, 11); resultSet = preparedStatement.executeQuery(); resultSet.next();
String name = resultSet.getString("name");
// 读取BLOB数据
Blob data = resultSet.getBlob("image"); // 获取字节流,将数据另存为一个文件
inputStream = data.getBinaryStream();
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("newPic.png"));
byte[] buf = new byte[1024];
int length = -1;
while((length = inputStream.read(buf)) != -1) {
bufferedOutputStream.write(buf, 0, length);
}
bufferedOutputStream.flush(); } catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
freeResource(inputStream, bufferedOutputStream);
freeResource(connection, preparedStatement, resultSet);
}
} /**
* 释放资源
* @param connection
* @param preparedStatement
*/
private static void freeResource(Connection connection, PreparedStatement preparedStatement) {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /**
* 释放资源
* @param connection
* @param preparedStatement
* @param resultSet
*/
private static void freeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} /**
* 释放资源
* @param reader
* @param bufferedReader
*/
private static void freeResource(InputStream inputStream, BufferedOutputStream bufferedOutputStream) {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
存blob数据的时候,文件过大存不下的解决方法
上面的代码,如果将过大的文件放进数据库,就会出现下面的错误:
Packet for query is too large (1245018 > 1048576).
You can change this value on the server by settin
上面的错误信息已经说得很明确了,问题在于MySQL对于每一个Packet的数据量的大小默认是不能超过1M的,要想解决这个问题,window下可以修改mysql的配置文件my.ini,在[mysqld]下面查找“max_allowed_packet”这一项,修改他的值即可;如果没有这一项,可以在最后加上这一项,比如要设置为16M,则增加“max_allowed_packet=16M” 即可
将数据库连接配置保存到外部properties文件中
数据库的连接配置需要设置driver、url、username、password,这些值都不应该在代码中写死,因为:
1、开发时使用的数据库和线上环境的数据库不相同(连接信息)。
2、一旦配置发生修改,就需要修改代码,很不方便,另外定位代码是个问题。
现在通常的做法是将配置信息都保存到一个文件中,程序在连接数据库的时候,读取该配置文件中的配置项即可。部署项目的时候,并不需要修改源码,只需要上传线上环境的配置文件即可。
下面是一个示例:
在src目录下,创建一个database.properties文件,内容如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
连接数据库时,可以这么做,但是不推荐:
package cn.ganlixin.test; import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties; public class Test {
public static void main(String[] args) throws Exception { Properties properties = new Properties();
properties.load(new FileInputStream("src/database.properties")); String driver = properties.getProperty("jdbc.driver");
String url = properties.getProperty("jdbc.url");
String username = properties.getProperty("jdbc.username");
String password = properties.getProperty("jdbc.password"); Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password); // ........
}
}
为什么说上面的做法不推荐呢?因为上面的做法中,是从读取的src目录下的database.properties文件,但是,我们在做项目的时候,项目编译后,执行的是是bin目录下字节码(*.class),而不是src下的源码(*.java),而我们写在src目录下的其他文件,都会被拷贝到bin目录下。所以,正确的方式,我们应该读取bin目录下的配置文件。
package cn.ganlixin.test; import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties; public class Test {
public static void main(String[] args) throws Exception { InputStream inputStream = null;
// 使用类加载器加载的配置文件
inputStream = Test.class.getClassLoader().getResourceAsStream("database.properties"); Properties properties = new Properties();
properties.load(inputStream); String driver = properties.getProperty("jdbc.driver");
String url = properties.getProperty("jdbc.url");
String username = properties.getProperty("jdbc.username");
String password = properties.getProperty("jdbc.password"); Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password); // ........
}
}