1. statement操作数据库(有SQL注入问题)
// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
public void testLogin() {
Scanner in=new Scanner(System.in);
System.out.print("请输入账户名:");
String userName=in.nextLine();
System.out.print("请输入密码:");
String password=in.nextLine();
String sql=
"SELECT user,password \n" +
"FROM user_table \n" +
"WHERE user='"+userName+"' AND password='"+password+"'";
User returnUser=get(sql,User.class);
if(returnUser!=null)
System.out.println("登录成功");
else System.out.println("登录失败");
}
1.1 SQL注入问题
// userName为1’ OR
// password为=‘1’ OR ‘1’ = '1
这时查询语句恒成立
SELECT USER,PASSWORD
FROM user_table
WHERE USER = '1' OR ' AND PASSWORD = '='1' OR '1' = '1';
2. preparedStatement(解决SQL注入问题)
PreparedStatement先将sql语句预处理,在向里面填充语句,解决sql注入问题,方面高效
2.1 添加数据操作
try,catch环绕ctrl,alt,t一起按下
// 向customers表中插入一条数据
public void insertTest() {
// 1.读取配置信息
Connection conn=null;
PreparedStatement ps=null;
try{
ResourceBundle rb=ResourceBundle.getBundle("jdbc");
String url=rb.getString("url");
String user= rb.getString("user");
String password=rb.getString("password");
String driverClass=rb.getString("driverClass");
// 2.加载驱动
Class.forName(driverClass);
// 3.建立连接
conn= DriverManager.getConnection(url,user,password);
System.out.println(conn);
// conn.commit();
// 4.预编译sql语句,返回preparedStatement实例
String sql="insert into customers(name,email,birth)values(?,?,?)";
ps=conn.prepareStatement(sql);
// 5.填充占位符,下标从1开始
ps.setString(1,"sad");
ps.setString(2,"sad@qq.com");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String st="2021-10-25";
java.util.Date d=sdf.parse(st);
ps.setDate(3,new java.sql.Date(d.getTime()));
6.执行操作
ps.execute();
}catch (Exception e){
e.printStackTrace();
}finally {
7.关闭连接
if(ps!=null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
2.2 添加数据操作的封装
工具类
// 获取连接
public static Connection getConnection() throws Exception {
// 1.资源配置的获取
ResourceBundle rb=ResourceBundle.getBundle("jdbc");
String url=rb.getString("url");
String user= rb.getString("user");
String password=rb.getString("password");
String driverClass=rb.getString("driverClass");
// 2.加载驱动
Class.forName(driverClass);
// 3.建立连接
Connection conn= DriverManager.getConnection(url,user,password);
return conn;
}
// 连接资源的关闭
public static void closeResource(Connection conn, PreparedStatement ps){
// 7.关闭连接
if(ps!=null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
封装后的实现代码
public void insertTest2() {
Connection conn=null;
PreparedStatement ps=null;
try{
// 1.获取数据库连接
conn=JDBCUtils.getConnection();
// 2.预编译sql语句,返回对象的实例
String sql="insert into customers(name,email,birth)values(?,?,?)";
ps=conn.prepareStatement(sql);
// 3.填充占位符,下标从1开始
ps.setString(1,"ccc");
ps.setString(2,"ccc@qq.com");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String st="2021-10-25";
java.util.Date d=sdf.parse(st);
ps.setDate(3,new java.sql.Date(d.getTime()));
4.执行操作
ps.execute();
}catch(Exception e){
e.printStackTrace();
}finally {
// 5,资源的关闭
JDBCUtils.closeResource(conn,ps);
}
}
2.3 通用的数据库增删改操作
如果表名或者其他字段和关键字重复,需要使用``来包裹起来该字段防止报错
例如:order是关键字,需要 order
通用增删改模板
// 增删改操作通用模板
public void updateTable(String sql,Object...vals){
// 可变形参vals的数量就是占位符的数量
Connection conn=null;
PreparedStatement ps=null;
try {
// 1.获取连接
conn=JDBCUtils.getConnection();
// 2,实例化PreparedStatement对象
ps=conn.prepareStatement(sql);
// 3.填充占位符
for(int i=0;i<vals.length;i++){
ps.setObject(i+1,vals[i]);
}
// 4.执行
ps.execute();//返回的是boolean类型,增删改返回false,查询返回true
//ps.exeUpdate();//返回的是更新的数目 int类型
} catch (Exception e) {
e.printStackTrace();
}finally {
// 5.关闭资源
JDBCUtils.closeResource(conn,ps);
}
}
执行语句
test t=new test();
// 增加
String sql0="insert into customers(name,email,birth) values(?,?,?)";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String str="2000-01-01";
Date d=null;
try {
d=sdf.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
t.updateTable(sql0,"ddd","ddd@qq.com",new java.sql.Date(d.getTime()));
// 删除
String sql1="delete from customers where name=?";
t.updateTable(sql1,"sad");
// 更改
String sql="update customers set name=? where id=?";
t.updateTable(sql,"莫扎特",18);
2.4数据库查询操作
起别名之后的where筛选条件还是只能使用原有表的字段
2.4.1 针对Customers表的查询操作
// 查询Customers表的数据
public void testQuery1(){
Connection conn=null;
PreparedStatement ps=null;
ResultSet resultSet=null;
try {
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句
String sql = "select id,`name`,email,birth from customers where id=?";
ps = conn.prepareStatement(sql);
// 3.填充占位符
ps.setInt(1, 36);
// 4.执行并返回结果集
resultSet = ps.executeQuery();
// 结果集的next()方法刚开始指针是指向,第一个元素前面的元素
// 返回boolean类型,有下一条数据,返回true指针下移
if (resultSet.next()) {
// 获取当前数据的所有字段值
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String email = resultSet.getString(3);
Date birth = resultSet.getDate(4);
// 将数据封装成一个对象
Customers c = new Customers(id, name, email, birth);
System.out.println(c);
}
}catch(Exception e){
e.printStackTrace();
}finally {
// 5.关闭资源
JDBCUtils.closeResource(conn,ps,resultSet);
}
}
2.4.2 Customers对象
部分代码
public class Customers {
private int id;
private String name;
private String email;
private Date birth;
public Customers(){}
public Customers(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
2.4.3 针对Customers表的通用查询操作
// Customers表的通用查询操作
public Customers testQuery2(String sql,Object...vals){
Connection conn= null;
PreparedStatement ps= null;
ResultSet rs= null;
try {
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句
ps = conn.prepareStatement(sql);
// 3.填充占位符
for(int i=0;i<vals.length;i++){
ps.setObject(i+1,vals[i]);
}
// 4.执行并返回结果集
rs = ps.executeQuery();
// 5.获取结果集的元数据
ResultSetMetaData rsmd=rs.getMetaData();
int columnCount=rsmd.getColumnCount();//知道返回结果有多少列
// 6.返回结果
if(rs.next()){
Customers cus=new Customers();
for(int i=0;i<columnCount;i++){
Object columnVal=rs.getObject(i+1);//返回这一列的值
String columnName=rsmd.getColumnName(i+1);//返回这一列的列名
// 反射为对象赋值
Field field=cus.getClass().getDeclaredField(columnName);//根据列名获取字段
// 需要DeclareField获取所有字段
field.setAccessible(true);//打破封装,外部也可以修改这个属性
field.set(cus,columnVal);
}
return cus;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 7.关闭连接
JDBCUtils.closeResource(conn,ps,rs);
}
return null;//这里仅当try没有返回时才去执行
}
// 如果try,finally中都有return,忽略try中的return
// try中的return保存在一个临时变量中,finally执行完毕后返回这个临时变量
执行语句和返回结果
test1 t=new test1();
String sql="select id,`name`,email,birth from customers where id=?";
System.out.println(t.testQuery2(sql,36));
String sql1="select id,`name`,email from customers where `name`=?";
System.out.println(t.testQuery2(sql1,"周杰伦"));
//返回结果
Customers{id=36, name='ddd', email='ddd@qq.com', birth=2000-01-01}
Customers{id=10, name='周杰伦', email='zhoujl@sina.com', birth=null}
2.4.4 针对Order表的通用查询操作
// 可以解决表中字段名和类中属性名不一致的情况
public Order testQuery1(String sql,Object...vals) {
Connection conn= null;
PreparedStatement ps= null;
ResultSet rs= null;
try {
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,占位符赋值
ps = conn.prepareStatement(sql);
for(int i=0;i<vals.length;i++){
ps.setObject(i+1,vals[i]);
}
// 3.获取结果集
rs = ps.executeQuery();//结果集存放的是数据
ResultSetMetaData rsmd=rs.getMetaData();//元数据存放的是修饰数据的内容
int columnCount=rsmd.getColumnCount();
// 4.生成结果集对象
if(rs.next()){
Order order=new Order();
for(int i=0;i<columnCount;i++){
Object columnVal=rs.getObject(i+1);
// 这里是获取mysql表的实际名称
// String columnName=rsmd.getColumnName(i+1);//这里的结果名是mysql表中的名称
// 获取mysql表的别名,如果没有别名,默认真实名称
String columnLable=rsmd.getColumnLabel(i+1);//这里结果是mysql表中的别名
// 需要Mysql表中的名称和类中的属性名一致,才能反射成功,需要使用别名
Field field=order.getClass().getDeclaredField(columnLable);
field.setAccessible(true);
field.set(order,columnVal);
}
return order;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 5.关闭资源连接
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
order对象
public class Order {
private int id;
private String name;
private Date date;
执行语句和返回结果
test2 t=new test2();
// 这里sql语句字段的别名要和类中属性名一致,使得反射成功
String sql="select order_id `id`,order_name `name`,order_date `date` from `order` where order_id=?";
System.out.println(t.testQuery1(sql,1));
//返回结果
Order{id=1, name='AA', date=2010-03-04}
2.4.5 查询操作流程
2.4.6 针对不同表的通用查询(返回一条记录)
// <T>表示这是一个泛型方法,返回类型为T,T类型由实参传入
public <T> T query1(Class<T> clazz,String sql,Object...vals){
Connection conn= null;
PreparedStatement ps= null;
ResultSet rs= null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,占位符赋值
ps = conn.prepareStatement(sql);
for(int i=0;i<vals.length;i++){
ps.setObject(i+1,vals[i]);
}
// 3.获取结果集
rs = ps.executeQuery();
ResultSetMetaData rsmd=rs.getMetaData();
int columnCount=rsmd.getColumnCount();
// 4,生成返回的结果集对象
if(rs.next()){
// 通过声明的构造器,new一个T类型的对象
T t=clazz.getDeclaredConstructor().newInstance();
for(int i=0;i<columnCount;i++){
Object columnVal=rs.getObject(i+1);
String columnLabel=rsmd.getColumnLabel(i+1);
// 反射为对象属性赋值
Field field=t.getClass().getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 5.关闭资源连接
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
执行语句和返回结果
test3 tes=new test3();
String sql1="select order_id `id`,order_name `name`,order_date `date` from `order` where order_id=?";
System.out.println(tes.query1(Order.class,sql1,1));
String sql2="select id,`name`,email,birth from customers where id=?";
System.out.println(tes.query1(Customers.class,sql2,36));
//返回结果
Order{id=1, name='AA', date=2010-03-04}
Customers{id=36, name='ddd', email='ddd@qq.com', birth=2000-01-01}
2.4.7 针对不同表的通用查询(返回多条记录)
public <T> List<T> query2(Class<T> clazz, String sql, Object...vals){
Connection conn= null;
PreparedStatement ps= null;
ResultSet rs= null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,占位符赋值
ps = conn.prepareStatement(sql);
for(int i=0;i<vals.length;i++){
ps.setObject(i+1,vals[i]);
}
// 3.获取结果集
rs = ps.executeQuery();
ResultSetMetaData rsmd=rs.getMetaData();
int columnCount=rsmd.getColumnCount();
// 4,生成返回的结果集对象
List<T> lists=new ArrayList<>();
while(rs.next()){//这里循环一次构造一个对象,即表的一行
// 通过声明的构造器,new一个T类型的对象
T t=clazz.getDeclaredConstructor().newInstance();
for(int i=0;i<columnCount;i++){
Object columnVal=rs.getObject(i+1);
String columnLabel=rsmd.getColumnLabel(i+1);
// 反射为对象属性赋值
Field field=t.getClass().getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnVal);
}
lists.add(t);
}
return lists;
} catch (Exception e) {
e.printStackTrace();
}finally {
// 5.关闭资源连接
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
执行语句
String sql3="select order_id `id`,order_name `name`,order_date `date` from `order`";
List lists3=tes.query2(Order.class,sql3);
lists3.forEach(o->System.out.println(o));
String sql4="select id,`name`,email,birth from customers";
List lists4=tes.query2(Customers.class,sql4);
lists4.forEach(o->System.out.println(o));