JDBC 实现Mysql数据库的CRUD操作
-
环境搭建
-
获取数据库连接
数据库配置文件:
使用配置文件形式:db.properties
url = jdbc:mysql://127.0.0.1:3306/0107a?
user = root
password = 888
读取properties配置文件
public static Connection getconnection() throws Exception {
//创建properties对象
Properties properties = new Properties();
//加载properties配置文件
properties.load(new FileInputStream("src/db.properties"));
//获取连接数据库的参数
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取链接
Connection conn = DriverManager.getConnection(url, user, password);
//返回链接
return conn;
}
使用配置文件形式:Mysql.xml形式
这种情况下,xml的标签可自由定义
<?xml version="1.0" encoding="UTF-8"?>
<mysql>
<url id="url">jdbc:mysql://127.0.0.1:3306/0107a?</url>
<user id="user">root</user>
<password id="pwd">888</password>
</mysql>
public static Connection getconnection() throws Exception {
SAXReader sr = new SAXReader();
//读取xml文件
Document document = sr.read("src/mysql.xml");
//获取xml文件根节点
Element rootElement = document.getRootElement();
System.out.println(rootElement.asXML());
//获取根节点下所有子节点
List<Element> elements = rootElement.elements();
String url = null;
String user = null;
String password = null;
for (Element element : elements) {
if(element.getName().equals("url")){
url = element.getText();
}else if(element.getName().equals("user")){
user = element.getText();
}else if(element.getName().equals("password")){
password = element.getText();
}
}
System.out.println(url+" "+user+" "+password);
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//获取链接
Connection conn = DriverManager.getConnection(url, user, password);
//返回链接
return conn;
}
两种方法均可获取mysql数据库的连接。
/**
* 关闭资源的方法
*/
public static void closeResore(ResultSet rs, Statement st, Connection conn) throws SQLException {
//关闭结果集
if(rs !=null){
rs.close();
}
//关闭语句体
if(st != null){
st.close();
}
//关闭连接
if(conn != null){
conn.close();
}
}
在关闭资源的过程中注意一定的顺序,先开后关,不然会出错。
- 封装简单的JDBCUtils
```java
/**
* 数据操作类
*/
public class JDBCUtils {
//增删改
public static int updateMethod(String sql, Object... args) throws Exception {
//执行增删改后影响的行数
int flag = 0;
//创建链接
Connection conn = null;
PreparedStatement pst = null;
//调用工具类获取链接
conn = JDBCTool.getconnection();
pst = conn.prepareStatement(sql);
//如果有参数,通过遍历为sql语句的占位符赋值
if(args != null){
for (int i = 0; i < args.length; i++) {
pst.setObject(i+1, args[i]);
}
}
//执行sql语句
flag = pst.executeUpdate();
//关闭资源
JDBCTool.closeResore(null, pst, conn);
return flag;
}
/**
* 执行查询语句
*/
public static <T> List<T> selectMethod(Class<T> clazz, String sql, Object... args) throws Exception {
//初始化查询实体
T entity = null;
//初始化链接
Connection connection = null;
//初始化语句体
PreparedStatement preparedStatement = null;
//初始化结果集
ResultSet resultSet = null;
//创建链表,用于装学生对象
List<T> list = new ArrayList<T>();
//获取链接
connection = JDBCTool.getconnection();
//获取语句体
preparedStatement = connection.prepareStatement(sql);
//如果参数列表不为空,则通过循环为sql中的?占位符赋值
if (args != null) {
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
}
//执行语句体,并接收查询的结果集
resultSet = preparedStatement.executeQuery();
//获取表的元数据集合(表中列的名称构成的集合)
ResultSetMetaData rsmd = resultSet.getMetaData();
//遍历结果集
while (resultSet.next()) {
//创建实体类对象(student),用于接收查询到的数据,将数据封装到student中 //通过反射创建对象
entity = clazz.newInstance();
//遍历元数据集
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//取出元数据集中的列名,并转换为string类型
String columnLabel = rsmd.getColumnLabel(i + 1);
//获取结果集的每列对应的值
Object columnValue = resultSet.getObject(i + 1);
//通过列名,获取实体类的成员变量列表
Field field = clazz.getDeclaredField(columnLabel);
//设置暴力访问
field.setAccessible(true);
//为实体类对象赋值
field.set(entity, columnValue);
}
//将实体类对象放入到链表中
list.add(entity);
}
//关闭资源
JDBCTool.closeResore(resultSet, preparedStatement, connection);
//返回list集合
return list;
}
}
4. 注意点。
PrepareStatement的用法
1.PreparedStatement是预编译(实例包含已编译的SQL语句)的,对于批量处理可以大大提高效率. 也叫JDBC存储过程
2.SQL语句可有一个IN或多个IN参数
---IN参数的值在SQL语句创建时未被指定,该语句为每一个IN参数保留一个问号(”?“)作为占位符
---每一个问号的值必须在该语句执行之前,通过适当的setInt或者setString方法提供。
与Statement的区别
1.Statement主要用于执行静态的SQL语句,内容固定不变。(对数据库只执行一次性存取)
2.PreparedStatement对象不仅包含了SQL语句,而且大多数情况下这个语句已经被预编译过,因而当其执行时,只需DBMS运行SQL语句,而不必先编译。当你需要执行Statement对象多次的时候,PreparedStatement对象将会大大降低运行时间,当然也加快了访问数据库的速度。
3.PrepareStatement这种转换也给你带来很大的便利,不必重复SQL语句的句法,而只需更改其中变量的值,便可重新执行SQL语句。选择PreparedStatement对象与否,在于相同句法的SQL语句是否执行了多次,而且两次之间的差别仅仅是变量的不同。如果仅仅执行了一次的话,它应该和普通的对象毫无差异,体现不出它预编译的优越性。
4.除了缓冲的问题之外,使用PreparedStatement对象,那就是安全性(预防SQL注入攻击 )。
---传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。
简单的sql注入攻击:
例如正常的delete语句 :delete from sysuser WHERE uid = '2'
恶意delete语句 : delete from sysuser WHERE uid = ' 前端传入的值 '
前端传入的值为:2 ' OR 1 = 1 -- ;此时的SQL语句为 delete from sysuser WHERE uid = ' 2 ' OR 1 = 1 -- '
由于--单行注释的存在,-- ' 被注释掉了;真正执行的SQL语句为 delete from sysuser WHERE uid = ' 2 ' OR 1 = 1,会将所有的数据库表中的数据全部删除
而使用预编译语句,最多最后写入数据库的值变为 2 ' OR 1 = 1 --ent对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。