Java 数据库操作
目录
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); // ........ } }