操作和访问数据库
在java.sql包中有3个接口分别定义了对数据库的调用的不同方式:
Statement:用于执行静态的SQL语句并返回所生成结果的对象
PreparedStatement:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效的执行该语句
CallableStatement:用于执行SQL存储过程
Statement
使用:通过Connection对象的createStatement方法创建Statement对象,该对象用于执行静态的SQL语句,并且返回执行结果。
Statement接口中执行SQL的方法:
- int executeUpdate(String sql) throws SQLException 执行给定的SQL语句,这可能是
INSERT
,UPDATE
,或DELETE
语句,或者不返回任何内容,如SQL DDL语句的SQL语句。 - ResultSet executeQuery(String sql) throws SQLException 执行给定的SQL语句,返回单个
ResultSet
对象。执行SELECT语句。
package com.statementANDresultset;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
/**
* @author 承夕
* @date 2020/2/26 0026 - 9:58
* @contact:https://github.com/chengxi0
*/
public class StatementResultSetDemo1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名");
String user_in = scanner.nextLine();
System.out.println("请输入密码");
String password_in = scanner.nextLine();
Connection connection = null ;
Statement statement = null ;
try {
//加载配置文件
InputStream inputStream = StatementResultSetDemo1.class.getClassLoader().getResourceAsStream("jdbc" +
".properties");
Properties properties = new Properties();
properties.load(inputStream);
//获取配置文件信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//加载注册驱动
Class.forName(driverClass);
//获取连接对象
connection = DriverManager.getConnection(url, user, password);
//获取Statement对象
statement = connection.createStatement();
//定义执行的SQL语句
/* String sql1 = "insert into `user` (name , password , address , phone) values ('zhangsan','123456','gd'," +
"'18888888888');";
String sql2 = "insert into `user` (name , password , address , phone) values ('lisi','234567','gd'," +
"'18888886666');";*/
String sql3 = "select `name` ,`password` from `user` where `name`='" + user_in + "' and `password` ='" + password_in + "';";
/* int i1 = statement.executeUpdate(sql1);
int i2 = statement.executeUpdate(sql2);*/
boolean b1 = statement.execute(sql3);
if (b1) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
/* System.out.println(i1);
System.out.println(i2);*/
} catch (IOException | ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
//释放资源
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
使用Statement的缺点:
- 问题一:存在拼串操作,繁琐
- 问题二:存在SQL注入问题(如上图所示),明明用户名错了,却还能登录进去
解决办法:
对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。
PreparedStatement
获取:调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象
使用:PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
好处:
- 代码可读性和可维护性大大提高。
- 它表示一条预编译的SQL语句,因此器有可能被重复利用,所以在被DBServer的编译器编译后的执行代码被缓存下来,下次使用的时候就不需要再编译,只要将参数直接传进去即可
- PreparedStatement可以方式SQL注入问题
package com.statementANDresultset;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.PropertyPermission;
/**
* @author 承夕
* @date 2020/2/28 0028 - 9:07
* @contact:https://github.com/chengxi0
*/
public class StatementResultSetDemo4 {
public static void main(String[] args) {
Connection connection = null ;
PreparedStatement preparedStatement = null ;
try {
//加载配置文件进内存
InputStream inputStream = StatementResultSetDemo4.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream);
//获取配置文件信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//加载注册驱动
Class.forName(driverClass);
//获取连接
connection = DriverManager.getConnection(url, user, password);
//获取PreparedStatement对象
preparedStatement = connection.prepareStatement("insert into `user` (`name`,`password`,`address`,`phone`) values (? ,? , ? ,?) ;");
preparedStatement.setString(1,"王五");
preparedStatement.setString(2,"345678");
preparedStatement.setString(3,"cx" );
preparedStatement.setString(4,"13243568456");
int i = preparedStatement.executeUpdate();
System.out.println(i);
} catch (IOException | ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
//释放资源
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
ResultSet
使用:通过Statement或者PreparedStatement对象的executeQuery()方法,
注意:
- 查询结果就是一个结果集对象.ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供.
- 实现 ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面
- ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。相当于Iterator对象的 hasNext() 和 next() 方法的结合体
- 当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值,索引是从1开始的
ResultSetMetaData
作用:通过ResultSet对象的getMetaData()方法获取,用于获取关于Result对象中列的类型和属性信息的对象
常用的方法:
- String getColumnName(int column) 获取指定列的名称,
column
- 第一列是1,第二列是2,... - String getColumnLabel(int column) 获取指定列的别名
- int getColumnCount() 返回此 ResultSet对象中的列数
- String getColumnTypeName(int column)检索指定列的数据库特定类型名称
- int getColumnDisplaySize(int column)指定指定列的正常最大宽度(以字符为单位)
- int isNullable(int column)表示指定列中的值是否可以为null值,返回值对应
columnNoNulls
columnNullable
columnNullableUnknown 的静态变量之一
- boolean isAutoIncrement(int column)指示指定列是否自动编号
JDBC工具类
package com.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @author 承夕
* @date 2020/2/28 0028 - 9:50
* @contact:https://github.com/chengxi0
*/
public class JDBCUtils {
public static Connection getConnection() {
Connection connection = null ;
try {
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//加载注册驱动
Class.forName(driverClass);
connection = DriverManager.getConnection(url, user, password);
} catch (IOException | SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
return connection ;
}
public static void closeResource(Connection conn, Statement stm, ResultSet st) {
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}if (stm != null) {
try {
stm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.statementANDresultset;
import com.domain.User;
import com.utils.JDBCUtils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* @author 承夕
* @date 2020/2/28 0028 - 10:19
* @contact:https://github.com/chengxi0
*/
public class StatementResultSetDemo5 {
public static void main(String[] args) {
String sql = "select * from `user` ;";
ArrayList<User> users = StatementResultSetDemo5.initObject(sql);
for (User u : users) {
System.out.println(u);
}
}
public static ArrayList<User> initObject(String sql, Object...args) {
ArrayList<User> users = new ArrayList<>();
Connection connection = null ;
PreparedStatement pstm = null ;
ResultSet rs = null ;
try {
//利用工具类获取数据库连接
connection = JDBCUtils.getConnection();
//获取PreparedStatement
pstm = connection.prepareStatement(sql);
//填充占位符
for (int i = 1; i <= args.length ; i ++) {
System.out.println(args[i -1]);
pstm.setObject(i, args[ i - 1 ]);
}
//获取结果集
rs = pstm.executeQuery();
Class<User> userClass = User.class;
User user = null ;
while (rs.next()) {
user = userClass.newInstance();
/* //利用反射进行对成员域初始化
Field name = userClass.getDeclaredField("name");
//忽略访问权限
name.setAccessible(true);
name.set(user,rs.getString(1));*/
Field[] fields = userClass.getDeclaredFields();
int i = 2 ;
for (Field f : fields) {
//忽略访问权限
f.setAccessible(true);
f.set(user, rs.getString(i++));
}
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}finally {
JDBCUtils.closeResource(connection,pstm,rs);
}
return users ;
}
}
注意:
- 利用传参中的Object...args这个可变参数,进行填充占位符?,这个方法不错.
- 字段,关键字不能填充到占位符