第三章 使用PreparedStatement实现CRUD操作
3.1 操作和访问数据库
数据库连接被用于数据库服务器发送命令和SQL语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。在java.sql包中有3个接口分别定义了对数据库的调用的不同方式: - Statement:用于执行静态SQL语句并返回它所生成结果的对象。 - PreparedStatement:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效的执行该语句。 - CallableStatement:用于执行SQL存储过程。
3.2 使用Statement操作数据表的弊端
通过调用Connection对象的createStatement()方法创建该对象。该对象用于执行静态的SQL语句,并返回执行结果。Statement接口中定义了下列方法用于执行SQL语句:
int excuteUpdate
(String
sql):执行更新操作
Insert、
update、
delete
ResultSet executeQuery
(String
sql):执行查询操作
Select
但是使用Statement操作数据表存在弊端:
问题1:存在拼串操作,繁琐问题2:存在SQL注入问题 SQL注入是利用某些系统没有队用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令。对于Java而言,要防范SQL注入,只要用PreparedStatement(从Statement扩展而来)取代Statement就可以了。
public static void main(String
[] args
) {
Scanner scanner
= new Scanner(System
.in
);
System
.out
.println("请输入用户名:");
String user
= scanner
.next();
System
.out
.println("请输入密码:");
String password
= scanner
.next();
String sql
= "select user,password from user_table where user = '"+user
+"'and password = '"+password
+"'";
User returnUser
= get(sql
,User
.class);
if(returnUser
!= null
){
System
.out
.println("登录成功");
}else{
System
.out
.println("用户名不存在或密码错误");
}
}
3.3 PreparedStatement的使用
3.3.1PreparedStatement介绍
3.3.2 使用Statement操作数据表的弊端
通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。
Statement 接口中定义了下列方法用于执行 SQL 语句: int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE ResultSet executeQuery(String sql):执行查询操作SELECT
使用Statement操作数据表存在弊端:
问题一:存在拼串操作,繁琐 问题二:存在SQL注入问题 SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法。
要防范 SQL 注入,只要用 PreparedStatemen取代 Statement 就可以了。
public static void main(String
[] args
) {
Scanner scanner
= new Scanner(System
.in
);
System
.out
.println("请输入用户名:");
String user
= scanner
.next();
System
.out
.println("请输入密码:");
String password
= scanner
.next();
String sql
= "select user,password from user_table where user = '"+user
+"'and password = '"+password
+"'";
User returnUser
= get(sql
,User
.class);
if(returnUser
!= null
){
System
.out
.println("登录成功");
}else{
System
.out
.println("用户名不存在或密码错误");
}
}
public static <T> T
get(String sql
, Class
<T> clazz
) {
T t
= null
;
Connection conn
= null
;
Statement st
= null
;
ResultSet rs
= null
;
try {
InputStream is
= StatementTest
.class.getClassLoader().getResourceAsStream("com/atguigu/statement/crud/jdbc.properties");
Properties pros
= new Properties();
pros
.load(is
);
String user
= pros
.getProperty("user");
String password
= pros
.getProperty("password");
String url
= pros
.getProperty("url");
String driverClass
= pros
.getProperty("driverClass");
Class
.forName(driverClass
);
conn
= DriverManager
.getConnection(url
, user
, password
);
st
= conn
.createStatement();
rs
= st
.executeQuery(sql
);
ResultSetMetaData rsmd
= rs
.getMetaData();
int columnCount
= rsmd
.getColumnCount();
if (rs
.next()) {
t
= clazz
.newInstance();
for (int i
= 0; i
< columnCount
; i
++) {
String columnName
= rsmd
.getColumnLabel(i
+ 1);
Object columnVal
= rs
.getObject(columnName
);
Field field
= clazz
.getDeclaredField(columnName
);
field
.setAccessible(true);
field
.set(t
, columnVal
);
}
return t
;
}
} catch (Exception e
) {
e
.printStackTrace();
} finally {
if (rs
!= null
) {
try {
rs
.close();
} catch (SQLException e
) {
e
.printStackTrace();
}
}
if (st
!= null
) {
try {
st
.close();
} catch (SQLException e
) {
e
.printStackTrace();
}
}
if (conn
!= null
) {
try {
conn
.close();
} catch (SQLException e
) {
e
.printStackTrace();
}
}
}
return null
;
}
3.3.1 Java与SQL对应数据类型转换表
3.3.4 使用PreparedStatement实现增、删、改操作
public static void main(String
[] args
) {
Connection conn
= null
;
PreparedStatement ps
= null
;
try {
InputStream is
= ClassLoader
.getSystemClassLoader().getResourceAsStream("com/atguigu/statement/crud/jdbc.properties");
Properties pros
= new Properties();
pros
.load(is
);
String user
= pros
.getProperty("user");
String password
= pros
.getProperty("password");
String url
= pros
.getProperty("url");
String driverClass
= pros
.getProperty("driverClass");
Class
.forName(driverClass
);
conn
= DriverManager
.getConnection(url
, user
, password
);
System
.out
.println(conn
);
String sql
= "insert into customers(name,email,birth) values(?,?,?)";
ps
= conn
.prepareStatement(sql
);
ps
.setString(1,"奥斯卡");
ps
.setString(2,"2836440891@qq.com");
SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
java
.util
.Date date
= sdf
.parse("1999-12-21");
ps
.setDate(3, new Date(date
.getTime()));
ps
.execute();
} catch (IOException e
) {
e
.printStackTrace();
} catch (ClassNotFoundException e
) {
e
.printStackTrace();
} catch (SQLException e
) {
e
.printStackTrace();
} catch (ParseException e
) {
e
.printStackTrace();
} finally {
try {
if(ps
!=null
)
ps
.close();
} catch (SQLException e
) {
e
.printStackTrace();
}
try {
if(conn
!=null
)
conn
.close();
} catch (SQLException e
) {
e
.printStackTrace();
}
}
}
@Test
public void testUpdate() {
Connection conn
= null
;
PreparedStatement ps
= null
;
try {
conn
= JDBCUtils
.getConnection();
String sql
= "update customers set name = ? where id = ?";
ps
= conn
.prepareStatement(sql
);
ps
.setObject(1,"莫扎特");
ps
.setObject(2,18);
ps
.execute();
} catch (Exception e
) {
e
.printStackTrace();
}finally {
JDBCUtils
.closeResource(conn
,ps
);
}
}
public void update(String sql
,Object
...args
){
Connection conn
= null
;
PreparedStatement ps
= null
;
try {
conn
= JDBCUtils
.getConnection();
ps
= conn
.prepareStatement(sql
);
for(int i
= 0; i
<args
.length
;i
++){
ps
.setObject(i
+1,args
[i
]);
}
ps
.execute();
} catch (Exception e
) {
e
.printStackTrace();
} finally {
JDBCUtils
.closeResource(conn
,ps
);
}
}
@Test
public void testCommonUpdate(){
String sql
= "update `order` set order_name = ? where order_id = ?";
update(sql
,"DD","2");
}
3.3.5 使用PreparedStatement实现查询操作
@Test
public void testQuery1() throws Exception
{
Connection conn
= JDBCUtils
.getConnection();
String sql
= "select id,name,email,birth from customers where id =?";
PreparedStatement ps
= conn
.prepareStatement(sql
);
ResultSet resultSet
= ps
.executeQuery();
if(resultSet
.next()){
int id
= resultSet
.getInt(1);
String name
= resultSet
.getString(2);
String email
= resultSet
.getString(3);
Date birth
= resultSet
.getDate(4);
Customer customer
= new Customer(id
, name
, email
, birth
);
System
.out
.println(customer
);
}
JDBCUtils
.closeResource(conn
,ps
,resultSet
);
}
}
3.4ResultSet与ResultSetMetaData
3.4.1 ResultSet
PreparedStatement 的 executeQuery()方法,查询结果是一个ResultSet 对象ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现 ResultSet 返回的实际上就是一张数据表,有一个指针指向数据表的第一条记录的前面。ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的next()方法移动到下一行。调用 next()方法检测下一行是否存在。若存在,该方法返回true,且指针下移,相当于Iterator对象的 hasNext() 和 next()方法的结合体。可以通过调用 getXxx()获取每一列的值
3.4.2 ResultSetMetaData
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象通过调用ResultSet对象的getMetaData()方法获得ResultSetMetaData对象 - getColumnName(int column):获取指定列的名称 - getColumnLabel(int column):获取指定列的别名 - getColumnCount():返回当前 ResultSet 对象中的列数。
3.5 资源的释放
- 释放ResultSet, Statement,Connection。
- 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统问题。Connection的使用原则是尽量晚创建,尽量早的释放。
- 可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。
3.6 JDBC API小结
两种思想
面向接口编程的思想
ORM思想(object relational mapping)
1. 一个数据表对应一个java类
2. 表中的一条记录对应java类的一个对象
3. 表中的一个字段对应java类的一个属性
两种技术
JDBC结果集的元数据:ResultSetMetaData
-获取列数:getColumnCount()
-获取列的别名:getColumnLabel()
通过反射,创建指定类的对象,获取指定的属性并赋值