JDBC 获取连接、增删改查操作
【摘要】 康师傅的程序里总要把数据库操作用try/catch包起来,不很理解 操作和访问数据库数据库连接被用于向数据库发送命令和SQL语句,并接受数据库服务器返回的结果。其实数据库连接就是一个Socket连接。在java.sql包中有三个接口分别定义了对数据库的调用的不同方式:Statement:用于执行静态SQL语句并返回他所生成的;PreparedStatement: SQL语句被 预编译并储存在...
康师傅的程序里总要把数据库操作用try/catch包起来,不很理解
操作和访问数据库
- 数据库连接被用于向数据库发送命令和SQL语句,并接受数据库服务器返回的结果。其实数据库连接就是一个Socket连接。
- 在java.sql包中有三个接口分别定义了对数据库的调用的不同方式:
- Statement:用于执行静态SQL语句并返回他所生成的;
- PreparedStatement: SQL语句被 预编译并储存在此对象中,可以使用此对象多次高效地执行该条语句;
- CallableStatement: 用于执行SQL存储过程。
JAVA与SQL对应数据类型转换表
JAVA类型 | SQL类型 |
---|---|
boolean | BIT |
byte | TINYINT |
short | SMALLINT |
int | INTEGER |
long | BIGINT |
String | CHAR,VARCHAR,LONGVARCHAR |
java.sql.Timestamp | BINARY,VAR BINARY |
byte array | DATE |
java.sql.Date | TIME |
java.sql.Time | TIMESTAMP |
Statement
方法
- 通过调用Connection对象的createStatement()方法创建该对象;
- Statement接口中定义了下列方法用于执行SQL语句
int excuteUpdate(String sql)
//执行更新操作INSERT、UPDATE、DELETE
ResultSet executeQuery(String sql)
//执行查询操作SELECT
使用Statement操作的弊端
-
拼接字符串操作繁琐
-
存在SQL注入问题
-
利用系统里没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句,完成恶意操作。
-
eg. SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’ (恒成立)
-
示例代码
public class StatementTest {
// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
@Test
public void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用户名:");
String userName = scan.nextLine();
System.out.print("密 码:");
String password = scan.nextLine();
//恶意注入操作
// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';
String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
+ "'";
//字符串连接操作繁琐
User user = get(sql, User.class);
if (user != null) {
System.out.println("登陆成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
// 使用Statement实现对数据表的查询操作
public <T> T get(String sql, Class<T> clazz) {
T t = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.加载配置文件
InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
// 2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 3.加载驱动
Class.forName(driverClass);
// 4.获取连接
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++) {
// //1. 获取列的名称
// String columnName = rsmd.getColumnName(i+1);
// 1. 获取列的别名
String columnName = rsmd.getColumnLabel(i + 1);
// 2. 根据列名获取对应数据表中的数据
Object columnVal = rs.getObject(columnName);
// 3. 将数据表中得到的数据,封装进对象
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;
}
}
PreparedStatement
- 是Connection对象中的方法,可以通过调用这个方法对sql语句进行操作,包括预编译、设置占位符等等;
- 可以通过调用 Connection 对象的 PreparedStatement(String sql) 方法获取PreparedStatement对象;
- PreparedStatement是Statement子接口,表示一条预编译SQL语句;
- PreparedStatement 对象所代表SQL语句中的参数用 ’ ?‘ 表示(占位符),通过调用 PreparedStatement 中的set方法来设置占位符。setXxx(索引,参数的值),注意数据库表格中的索引是从1开始的。
PreparedStatement优点
- 代码的可读性和可维护性
- 防止 SQL 注入
- 可以最大可能提高性能
- 数据库服务器会对编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被数据库服务器编译后执行代码会被缓存下来,那么下次调用相同的预编译语句时就不要编译,是需要将参数直接传入编译过的语句执行代码中,就会得到执行;
- 在Statement语句中即使是相同的操作,但会因为数据内容的不一样,所以整个语句本身不能匹配,没有缓存语句的意义,数据库服务器也不会对普通语句编译后的执行代码缓存,这样每执行一次就要对传入的语句编译一次。
打开和关闭资源的封装
- 执行每一个对数据库的操作之前,都需要打开驱动和连接,操作完之后都需要关闭资源,因此可以对这两步操作进行封装——封装进工具类(JDBCUtils)里。
public class JDBCUtils(){
public static Connection getConnection() throws Exception {
//1.加载配置文件
InputStream is= ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2.读取配置信息
String user=pros.getProperty("user");
String password = pros.getProperty("password");
String url= pros.getProperty("url");
String driverClass=pros.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
Connection conn = null;
try {
conn = DriverManager.getConnection(url,user,password);
System.out.println("数据库连接创建成功!");
} catch (SQLException e) {
e.printStackTrace();
System.out.println("数据库连接创建失败!");
}
return conn;
}
//public static void closeResourse(Connection conn, PreparedStatement ps){}
//PreparedStatement是statement子接口,则定义的时候可以用大一些的类来定义
public static void closeResourse(Connection conn, Statement ps){
try {
if (ps != null) {
ps.close();
}
}catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null){
conn.close();
}
}catch (SQLException e) {
e.printStackTrace();
}
}
//重载关闭资源的方法
//针对查询操作还要多关闭一个结果集
public static void closeResourse(Connection conn, Statement ps,ResultSet rs){
try {
if (ps != null) {
ps.close();
}
}catch (SQLException e) {
e.printStackTrace();
}
try {
if (rs != null) {
rs.close();
}
}catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null){
conn.close();
}
}catch (SQLException e) {
e.printStackTrace();
}
}
}
增删改
-
思考无返回值的增删改查操作,每次操作无非是获取连接、预编译SQL语句、填充占位符、执行语句、关闭资源,则可以对这三个操作进行封装,操作时将增删改查不同的SQL语句和占位符传进方法即可。
package indi.jdbc.demo; import utility.JDBCutils; import java.sql.Connection; import java.sql.PreparedStatement; /** * Created by Intellij IDEA. * User: ZouYue * Date: 2022/4/23 18:55 * @author ZouYue */ public class PreparedStatementUpdateTest { JDBCutils jdbCutils= new JDBCutils(); public static void main(String[] args) { /*String sql1 = "insert into student (name,email,birth,id) values(?,?,?,?)"; if (update(sql1,"米老鼠","MLS@milao.com","3001-01-01",17)) { System.out.println("数据添加成功!"); }*/ /*String sql2 = "insert into student (name,email,birth,id) values(?,?,?,?)"; if (update(sql2,"灰太狼","HTL@huitai.com","4001-01-01",18)) { System.out.println("数据添加成功!"); }*/ /*String sql3 = "delete from student where name=?"; if (update(sql3,"灰太狼")) { System.out.println("数据删除成功!"); }*/ String sql4="update student set birth=? where name=?"; if (update(sql4,"4021-09-09","米老鼠")) { System.out.println("数据修改成功!"); } } public static boolean update(String sql, Object... args){ //用可变形参获取占位符 //可变形参可以看成一个数组 Connection conn=null; PreparedStatement ps=null; try { //1.获取连接 conn= JDBCutils.getConnection(); //2.预编译sql语句,返回PreparedStatement对象 ps = conn.prepareStatement(sql); //3.填充占位符 for(int i = 0; i < args.length; i++){ //注意下标:数据库中是从1开始,而数组中是从0开始 ps.setObject(i+1,args[i]); } //4.执行语句 ps.execute(); }catch (Exception e) { e.printStackTrace(); return false; }finally { //5.关闭资源 JDBCutils.closeResourse(conn,ps); } return true; } }
查询
OMR编程思想(object relational mapping)
- 一个数据表对应一个java类
- 表中的一条记录对应java类的一个对象
- 表中的一个字段对应java类的一个属性
针对某张表中单条数据的查询操作
package indi.jdbc.demo;
import bean.Customer;
import utility.JDBCutils;
import java.lang.reflect.Field;
import java.sql.*;
/**
* Created by Intellij IDEA.
* User: ZouYue
* Date: 2022/4/23 20:17
* @author ZouYue
*/
public class PreparedStatementQuery {
static JDBCutils jdbcutils=new JDBCutils();
public static void main(String[] args) throws Exception {
//testQuery1();
String sql = "select * from student where id=?";
System.out.println(testQuery2(sql,2).print());
}
//针对student表数据的通用查询操作
public static Customer testQuery2(String sql,Object...args) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCutils.getConnection();
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
//ResultSetMetaData获取结果集的数据元
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中元素的个数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
Customer customer=new Customer();
for (int j = 0; j < columnCount; j++) {
//获取列值,因为不知道数据类型,则直接用object获取
Object columnValue=rs.getObject(j+1);
//获取每个列的名字
String columnName=rsmd.getColumnName(j+1);
//通过反射,给customer对象中给定的columnName属性,赋值为columnValue
Field field=Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(customer,columnValue);
}
return customer;
}
}catch (Exception e) {
e.printStackTrace();
}finally {
JDBCutils.closeResourse(conn,ps,rs);
}
return null;
}
//针对student表的数据的查询操作
public static void testQuery1(){
Connection conn = null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//1.获取连接
conn = JDBCutils.getConnection();
//2.SQL语句,通过PreparedStatement进行SQL语句的操作
String sql = "select * from student";
ps=conn.prepareStatement(sql);
//3.执行语句:返回结果集(表格中的一行数据)
rs=ps.executeQuery();
//4.处理结果集
//while用于多条记录的查询
//if用于单条记录的查询
//next判断是否有下一条数据,有true,没有false
while (rs.next()) {
//获取当前记录中的各个字段
String name = rs.getString(1);
String email = rs.getString(2);
Date birth=rs.getDate(3);
int id=rs.getInt(4);
//将些数据封装进一个类里
Customer customer = new Customer(name,email,birth,id);
System.out.println(customer.print());
}
}catch (Exception e) {
e.printStackTrace();
}finally {
//5.关闭资源
JDBCutils.closeResourse(conn,ps,rs);
}
}
}
通过getColumnLabel解决表中列名与属性名不同的情况
-
SQL语句对列名重命名;
-
当遇到表格列明和类中属性不同名的情况,获取列名的时候,直接使用getColumnLabel获取相应属性名(此方法更常用也更安全);
package indi.jdbc.demo;
import bean.CustomerTest;
import utility.JDBCutils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
/**
* Created by Intellij IDEA.
* User: ZouYue
* Date: 2022/4/24 16:03
* @author ZouYue
*/
public class PreparedStatementQuery2 {
static JDBCutils jdbcutils=new JDBCutils();
public static void main(String[] args){
//对表格和类中名字不同的属性,进行重命名
String sql="select name name1,email email1,birth birth1,id id1 from student where id=?";
System.out.println(testQuery3(sql,1).print());
}
// 针对表格中列名与类中属性不同的情况
// getColumnLabel()的使用
public static CustomerTest testQuery3(String sql,Object...args) throws Exception {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs = null;
try{
//1.获取连接
conn = JDBCutils.getConnection();
ps= conn.prepareStatement(sql);
//2.填充占位符
for(int i = 0; i < args.length; i++){
ps.setObject(i+1, args[i]);
}
//3.获取数据元
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount=rsmd.getColumnCount();
if (rs.next()){
CustomerTest ct=new CustomerTest();
for(int j = 0; j < columnCount; j++){
//获取数据元
Object columnValue=rs.getObject(j+1);
//获取列名
//String columnName = rsmd.getColumnName(j+1);
//由于表格中列名和CustomerTest中属性的名字不一样,报错:NoSuchFieldException
//更安全的用法应当用getColumnLabel()获取列的标签,也就是CustomerTest中定义的属性名
//且当名称不一样时,对应sql语句中也要对筛选的数据进行重命名
String columnLabel= rsmd.getColumnLabel(j+1);
//通过反射,将获取的数据封装到类中
Field field=CustomerTest.class.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(ct,columnValue);
}
return ct;
}
}catch (Exception e) {
e.printStackTrace();
}finally {
JDBCutils.closeResourse(conn,ps,rs);
}
return null;
}
}
针对某张表中查询的多条数据
// 通用的针对于不同表的查询:返回一个对象 (version 1.0)
public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,得到PreparedStatement对象
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
// 4.执行executeQuery(),得到结果集:ResultSet
rs = ps.executeQuery();
// 5.得到结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 6.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {// 遍历每一个列
// 获取列值
Object columnVal = rs.getObject(i + 1);
// 获取列的别名:列的别名,使用类的属性名充当
String columnLabel = rsmd.getColumnLabel(i + 1);
// 6.2使用反射,给对象的相应属性赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7.关闭资源
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
查询操作的流程
针对不同表的单条记录的查询操作
package indi.jdbc.demo;
import bean.Customer;
import utility.JDBCutils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
/**
* Created by Intellij IDEA.
* User: ZouYue
* Date: 2022/4/25 20:42
* @author ZouYue
*/
public class PreparedStatementQuery3 {
public static void main(String[] args) {
String sql="select * from customer where id=?";
System.out.println(testQuery3(Customer.class,sql, 1).print());
}
// 使用PreparedStatement针对不同表的查询单条数据的操作
// 使用泛型
public static <T> T testQuery3(Class<T> clazz, String sql, Object...args){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs = null;
try{
//1.获取连接
conn = JDBCutils.getConnection();
ps= conn.prepareStatement(sql);
//2.填充占位符
for(int i = 0; i < args.length; i++){
ps.setObject(i+1, args[i]);
}
//3.获取数据元
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount=rsmd.getColumnCount();
if (rs.next()){
//对于java中任何一个类,通过此方法调用一个空参构造器
//但是在我的IDEA中说是已过时,不懂
T t= clazz.newInstance();
for(int j = 0; j < columnCount; j++){
Object columnValue=rs.getObject(j+1);
String columnLabel= rsmd.getColumnLabel(j+1);
Field field= clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
}catch (Exception e) {
e.printStackTrace();
}finally {
JDBCutils.closeResourse(conn,ps,rs);
}
return null;
}
}
针对不同表的多条记录的查询操作
- 将查找到的多条数据存放到集合中返回
package indi.jdbc.demo;
import bean.Customer;
import utility.JDBCutils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Intellij IDEA.
* User: ZouYue
* Date: 2022/4/25 20:42
* @author ZouYue
*/
public class PreparedStatementQuery3 {
public static void main(String[] args) {
String sql2="select * from customer where id<?";
List<Customer> listCustomer = testQuery4(Customer.class,sql2,5);
for (Customer c : listCustomer) {
System.out.println(c.print());
}
}
// 使用PreparedStatement针对不同表的查询多条数据的操作
// 将多条数据存到集合里
// 查找不到数据:①list.size()=0;②异常
public static <T> List<T> testQuery4(Class<T> clazz, String sql, Object...args){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs = null;
try{
//1.获取连接
conn = JDBCutils.getConnection();
ps= conn.prepareStatement(sql);
//2.填充占位符
for(int i = 0; i < args.length; i++){
ps.setObject(i+1, args[i]);
}
//3.获取数据元
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount=rsmd.getColumnCount();
ArrayList<T> list = new ArrayList<T>();
while (rs.next()){
//对于java中任何一个类,通过此方法调用一个空参构造器
T t= clazz.newInstance();
for(int j = 0; j < columnCount; j++){
Object columnValue=rs.getObject(j+1);
String columnLabel= rsmd.getColumnLabel(j+1);
Field field= clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
}catch (Exception e) {
e.printStackTrace();
}finally {
JDBCutils.closeResourse(conn,ps,rs);
}
return null;
}
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)