Java 数据库操作

目录


Java数据库组织架构

下载驱动包

连接数据库

  连接数据库的三个步骤

  连接数据库的高开销

Statement接口介绍

PreparedStatement类

  使用PreparedStatement进行DML操作

  使用PreparedStatement进行DQL操作

JDBC事务处理

批量SQL操纵

数据库存取文本内容的大对象(CLOB)

数据库存取二进制的大对象(BLOB)

存blob数据的时候,文件过大存不下的解决方法

将数据库连接配置保存到外部properties文件中

 

 

 

 


 

 

 

 

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);
		
		// ........
	}
}

  

 

posted @ 2018-11-04 13:26  寻觅beyond  阅读(464)  评论(0编辑  收藏  举报
返回顶部