文章目录
JAVA数据库连接方式JDBC
简言
近来由于项目开发需要,对JAVA语言有一定要求。计划通过一个简易练习项目-商超购物管理系统,分别构建控制台、原生网站项目以及Springboot框架构建网站项目,逐步达到简单使用JAVA语言的目的。
本项目是多个项目的合集,目的在于记录自己不同层次下的简单练习过程,便于个人后续学习与思考。
1. JDBC简单介绍
JDBC为一个JAVA API,可以通过JDBC驱动程序将JAVA数据类型转化为适当的数据库类型,当从PrepareStatement
或CallableStatement
对象或ResultSet.updateXXX()/getXXX()
方法调用时,将JAVA数据类型映射为数据库数据类型
SQL | JDBC / Java | setXXX | setXXX… | updateXXX |
---|---|---|---|---|
VARCHAR | java.lang.String | setString | getString | updateString |
CHAR | java.lang.String | setString | getString | updateString |
LONGVARCHAR | java.lang.String | setString | updateString | |
BIT | boolean | setBoolean | getBoolean | updateBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | getBigDecimal | updateBigDecimal |
TINYINT | byte | setByte | getByte | updateByte |
SMALLINT | short | setShort | getShort | updateShort |
INTEGER | int | setInt | getInt | updateInt |
BIGINT | long | setLong | getLong | updateLong |
REAL | float | setFloat | getFloat | updateFloat |
FLOAT | float | setFloat | getFloat | updateFloat |
DOUBLE | double | setDouble | getDouble | updateDouble |
VARBINARY | byte [] | setBytes | getBytes | updateBytes |
BINARY | byte[] | setBytes | getBytes | updateBytes |
DATE | java.sql.Date | setDate | getDate | updateDate |
TIME | java.sql.Time | setTime | getTime | updateTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | getTimestamp | updateTimestamp |
CLOB | java.sql.Clob | setClob | getClob | updateClob |
BLOB | java.sql.Blob | setBlob | getBlob | updateBlob |
ARRAY | java.sql.Array | setARRAY | getARRAY | updateARRAY |
REF | java.sql.Ref | setRef | getRef | updateRef |
STRUCT | java.sql.Struct | setStruct | getStruct | updateStruct |
注册JDBC驱动程序
为了连接到数据库系统,我们需要在程序中注册数据库系统的驱动程序。
驱动程序名称与数据库系统相关。
本文采用Mysql数据库,因此需要Mysql数据库连接驱动。
没有安装的小伙伴可以通过这个链接获取 :
https://pan.baidu.com/s/1lkfTpf2nloo0WW9CduiuNw 提取码: qw78
如果需要根据版本下载可以通过这个链接:
https://repo1.maven.org/maven2/mysql/mysql-connector-java/
驱动程序配置参考:
https://www.cnblogs.com/centor/p/6142775.html
数据库URL
加载驱动程序后,我们可以通过DriverManager.getConnection()
方法打开一个连接。
有三个重载的DriverManager.getConnection()
方法:
- getConnection(String url)
- getConnection(String url,Properties prop)
- getConnection(String url,String user,String password)
数据库URL是指向数据库的地址。
String driver = "com.mysql.jdbc.Driver";
String URL = "jdbc:mysql://localhost:3306/test";
String USER = "root";
String PASS = "password"
Connection conn = DriverManager.getConnection(URL, USER, PASS);
关闭JDBC连接
在退出JDBC应用程序前,我们需要显式关闭与数据库的所有连接,以结束每个数据库会话。为了确保连接关闭,在finally块中放置close()
方法,因为finally块始终运行,而且不管是否发生。
try{
rs.close()
}catch(SQLException e) {
//数据库连接失败异常处理
e.printStackTrace();
}finally{
connection.close()
}
2. JDBC语句
获取连接后,我们可以使用sql语句与数据库交互。
通过sql语句由从Connection返回的Statement
接口和PreparedStatement
接口执行。
JDBC的Statement语句
用于通用访问数据库。在使用静态SQL语句时非常有用。
- Statement接口不能接收参数。
- Statement对象使用Connection对象的createStatement()方法创建.
Statement stmt = conn.createStatement( );
SQL语句执行
我们可以通过Statement对象的三个execute方法执行一个SQL语句
-
boolean execute(String SQL)
执行SQL语句,如create database,create table等.如果可以检索到ResultSet对象,则返回true,否则返回false。
-
int exectuteUpdate(String SQL)
执行影响行列值的SQL语句,例如INSERT、UPDATE和DELETE语句。返回受SQL语句影响行数。
-
ResultSet executeQuery(
String SQL
)执行SELECT语句并返回ResultSet对象,通过ResultSet对象的next()方法获取。
Statement语句对象的关闭
为了清理分配的数据库资源,我们需要关闭使用的Statement对象。
Statement stmt = null;
try {
stmt = conn.createStatement( );
stmt execuate method();
}
catch (SQLException e) {
}
finally {
stmt.close();
}
3. JDBC预执行语句
-
PreparedStatement用于多次执行SQL语句。
-
PreparedStatement接口在运行时接受输入参数。
-
PreparedStatement接口扩展了Statement接口。
我们可以为PreparedStatement动态提供参数。
PreparedStatement对象创建
PreparedStatement pstmt = null;
try {
String SQL = "Update goods SET gname = ? WHERE gid = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
JDBC中的所有参数都由?
符号。
?
被称为参数标记。
我们必须在执行SQL语句之前为每个参数提供值。
PreparedStatement
中的setXXX()
方法将值绑定到参数,其中XXX
表示Java数据类型。
与Java数组或List中的集合框架不同。PreparedStatement
中的参数索引从位置1开始。
PreparedStatement对象的关闭
我们需要关闭PreparedStatement对象以释放为其分配的资源。
关闭Connection对象也将关闭PreparedStatement对象。
PreparedStatement pstmt = null;
try {
String SQL = "Update goods SET gname = ? WHERE gid = ?";
pstmt = conn.prepareStatement(SQL);
}
catch (SQLException e) {
}
finally {
pstmt.close();
}
4. PrepareStatement与Statement语句区别
- PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程。Statement是直接执行SQL语句进行数据类型映射,对于单条SQL语句来讲执行效率远远快于PrepareStatement语句。
- Statement每次执行SQL语句,相关数据库都要执行SQL语句的编译,PrepareStatement是预编译语句,支持批处理。
- PrepareStatement对象不必重复SQL语句的句法,而只需要改变其中变量的值,便可以重新执行SQL语句。
示例1:
String updateString = "UPDATE goods SET gnum = 75 " + "WHERE gname LIKE ′coffee′";
stmt.executeUpdate(updateString);
示例2:
PreparedStatement updateSales = con.prepareStatement("UPDATE goods SET gnum = ? " + "WHERE gname LIKE ?");
updateSales.setInt(1, 75);
updateSales.setString(2, "coffee");
updateSales.executeUpdate();
示例2和示例1的区别在于后者使用PrepareStatement对象,前者使用Statement对象,通过参数的使用可以使SQL语句利用率提高。
还有一个更好的原因使我们在企业应用程序中更喜欢使用PreparedStatement对象,那就是安全性。传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。
当处理公共Web站点上的用户传来的数据的时候,安全性的问题就变得极为重要。传递给PreparedStatement的字符串参数会自动被驱动器忽略。特别是在公共Web站点上,在没有首先通过PreparedStatement对象处理的情况下,所有的用户输入都不应该传递给SQL语句。
示例:
sql = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"
恶意填入:
userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";
那么最终SQL语句变成了:
sql = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
总结:
- PrepareStatement会先初始化SQL,先把这个SQL提交到数据库中进行预处理,多次使用可提高效率。 Statement不会初始化,没有预处理,没次都是从0开始执行SQL。
- PrepareStatement可以替换变量 ,Statement通过写SQL语句实现目的。
- PrepareStatement预编译会考虑数据类型。
5. JDBC存储过程语句
-
CallableStatement接口可以接受运行时输入参数。
-
CallableStatement用于执行数据库存储过程。
-
Connection对象也可以创建CallableStatement来调用数据库存储过程。
示例:
假设,存在以下存储过程
DELIMITER $$
DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$
CREATE PROCEDURE `EMP`.`getEmpName`
(IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255))
BEGIN
SELECT first INTO EMP_FIRST FROM Emp WHERE ID = EMP_ID;
END $$
DELIMITER ;
上面的存储过程定义了两个参数,一个是EMP_ID,另一个是EMP_FIRST。它通过员工id返回员工的名字。
IN和OUT之前的参数名称告诉参数的类型。 IN用于数据输入,OUT用于数据输出。
CallableStatement对象参数
参数 | 描述 |
---|---|
IN | 创建SQL语句时其值未知的参数。我们使用setXXX()方法将值传递给IN参数。 |
OUT | 从SQL语句返回其值的参数。我们使用getXXX()方法从OUT参数中获取值。 |
INOUT | 参数可以传入和传出值。我们用setXXX()方法绑定变量,并用getXXX()方法检索值。 |
创建存储过程、
CallableStatement cstmt = null;
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
关闭存储过程
CallableStatement cstmt = null;
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
cstmt.close();
6. JDBC结果集
JDBCjava.sql.ResultSet
用于处理从SQL select语句返回的结果。
-
SQL select语句从数据库读取数据,并返回结果集中的数据。
-
select语句的结果以表格形式显示。它有列和行。
-
ResultSet对象维护一个指向结果集中当前行的游标。
对于某一行,我们可以使用java.sql.ResultSet
中的方法来获取数据逐列。
ResultSet中的方法
ResultSet接口的方法有三个类别:
- Navigational方法来回移动光标。
- Getter方法从当前行获取数据。
- Update方法更新当前行的数据。
ResultSet类型
类型 | 描述 |
---|---|
ResultSet.TYPE_FORWARD_ONLY | 光标只能向前移动。 |
ResultSet.TYPE_SCROLL_INSENSITIVE | 光标可以向前和向后滚动。 ResultSet对在创建ResultSet后对数据库所做的更改不敏感。 |
ResultSet.TYPE_SCROLL_SENSITIVE | 光标可以向前和向后滚动。并且ResultSet对在创建结果集后对数据库所做的更改很敏感。 |
-
基本ResultSet
这个ResultSet他起到的作用就是完成了查询结果的存储功能,而且只能读去一次,不能够来回的滚动读取。
Statement st = conn.CreateStatement ResultSet rs = Statement.excuteQuery(sqlStr)
-
可滚动的ResultSet
这个类型支持前后滚动取得纪录next()、previous(),回到第一行first(),同时还支持要去的ResultSet中的第几行 absolute(int n),以及移动到相对当前行的第几行relative(int n)
Statement st = conn. createStatement (int resultSetType, int resultSetConcurrency) ResultSet rs = st.executeQuery(sqlStr)
-
可更新的ResultSet
这样的ResultSet对象可以完成对数据库中表的修改能够完成更新的ResultSet的SQL语句必须要具备如下的属性:
- 只引用了单个表。
- 不含有join或者group by子句。
- 那些列中要包含主关键字。
Statement st = createstatement(Result.TYPE_SCROLL_INSENSITIVE,Result.CONCUR_UPDATABLE)
-
可保持的ResultSet
正常情况下如果使用Statement执行完一个查询,又去执行另一个查询时这时候第一个查询的结果集就会被关闭,也就是说,所有的Statement的查询对应的结果集是一个,如果调用Connection的commit()方法也会关闭结果集。可保持性就是指当ResultSet的结果被提交时,是被关闭还是不被关闭。
ResultSet导航
我们可以使用ResultSet接口中的以下方法来移动光标。
方法 | 描述 |
---|---|
beforeFirst() | 将光标移动到第一行之前 |
afterLast() | 将光标移动到最后一行之后 |
first() | 将光标移动到第一行 |
last() | 将光标移动到最后一行 |
绝对(int row) | 将光标移动到指定的行 |
relative(int row) | 相对于光标所在位置向前或向后移动光标行数。 |
previous() | 将光标移到上一行。 |
next() | 将光标移动到下一行。 |
int getRow() | 返回游标指向的行号。 |
moveToInsertRow() | 将光标移动到我们可以将新行插入数据库的位置。当前行号不更改。 |
moveToCurrentRow() | 如果光标当前位于插入行,则将光标移回当前行;否则,此方法不执行任何操作。 |