zoukankan      html  css  js  c++  java
  • 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);
    		
    		// ........
    	}
    }
    

      

  • 相关阅读:
    5G(NR)无线网络协议栈 (层2和层3)
    tcp,udp报文最大长度
    MSS与MTU的关系
    5G网络(接入网+承载网+核心网)
    华为发布:5G时代十大应用场景白皮书(附下载)
    爱码仕 解读5G(八)再见了,SIM卡
    爱码仕 解读5G (七)无线娱乐在家里、在车里、在加油站、在充电站、在高速公路休息区
    爱码仕 解读5G (六)健康管理和无线医疗
    爱码仕 解读5G (五)能源领域 电力馈线自动化 智慧油田 智慧海洋
    在DevExpress中使用CameraControl控件进行摄像头图像采集
  • 原文地址:https://www.cnblogs.com/-beyond/p/9903680.html
Copyright © 2011-2022 走看看