一.对数据库操作的简介
1.操作数据库的接口
①statement(静态sql语句)
②preparedstatement(SQL语句预编译)
③callablestatement(执行SQL的存储过程)
⑤CRUD操作
- create retrieve update delete
2.statement出现的问题
①拼接字符串麻烦
需要显示的拼接字符串
②SQL注入问题:没有检查语句
- 实际语句:where user = ‘1’ OR ‘AND password=’ = 1 OR ‘1’='1’此语句必然返回true
- 拼接语句:where user = ’ “+user+” ’ AND password =’ " +password+ " ’
- 输入语句:user:1’ OR password:= 1 OR ‘1’='1
- 此时产生歧义,会导致无论如何都能够登录成功
二.使用preparedstatement来实现增删改查
1.增删改(无返回值)
①数据库连接获得connection
- 获取配置文件信息
- 注册驱动类的对象
- 获取connection
②利用connection获取preparedstatement实例
- 获取preparedstatement实例‘
- 定义预编译sql语句
- 填充sql语句
- 执行实例
③关闭资源
- preparedstatement
- connection
④代码举例(向customers添加一条记录)
//向customers添加一条记录
@Test
public void testInsert() throws Exception{
//1.获取配置文件信息(加载配置文件+读取配置文件)
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("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");
//2.注册驱动类的对象(加载运行时类和注册运行时类对象)
Class.forName(driverClass);
//3.获取connection
Connection conn = DriverManager.getConnection(url, user, password);
//System.out.println(conn);
//4.预编译sql语句,返回preparestatement实例
String sql = "insert into customers(name,email,birth) values(?,?,?)";//?占位符
PreparedStatement ps = conn.prepareStatement(sql);
//5.填充占位符
ps.setString(1, "哪吒");
ps.setString(2, "enzha@qq.com");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1000-01-01");
ps.setDate(3, new Date(date.getTime()));
//6.执行操作
ps.execute();
//7.资源关闭
ps.close();
conn.close();
}
⑤代码举例(适用于任何表的增删改)
+通用获取连接和关闭连接操作
//获取数据库资源
public static Connection getConnection() throws Exception{
//获取配置文件信息(加载配置文件+读取配置文件)
//1.获取配置文件信息(加载配置文件+读取配置文件)
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("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");
//2.注册驱动类的对象(加载运行时类和注册运行时类对象)
Class.forName(driverClass);
//3.获取connection
Connection conn = DriverManager.getConnection(url, user, password);
//System.out.println(conn);
return conn;
}
//关闭数据库资源
public static void closeResource(Connection conn,Statement ps){
try{
if(ps != null)
ps.close();
}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn != null)
ps.close();
}catch(SQLException e){
e.printStackTrace();
}
}
- 通用更新操作
```sql
//通用的更新操作
public static void update(String sql,Object ...args) throws Exception{//sql占位符个数与可变形参长度一直
//1.获取数据库的链接
Connection conn = JDBCUtils.getConnection();
//2.预编译sql语句返回preparedstatement实例(已经知道要干什么事儿)
PreparedStatement ps = conn.prepareStatement(sql);
//3.填充sql占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1, args[i]);
}
//4.执行
ps.execute();
//5.关闭资源
JDBCUtils.closeResource(conn, ps);
}
public void testCommon() throws Exception{
String sql ="delete from customers where id = ?";
JDBCUtils.update(sql, 3);
//注意表名如果是关键字加上着重号
}
2.查询(有返回值)
①数据库连接获得connection
- 获取配置文件信息
- 注册驱动类的对象
- 获取connection
②利用connection获取preparedstatement实例
- 获取preparedstatement实例‘
- 定义预编译sql语句
- 填充sql语句
- 执行实例并返回结果集
③利用结果集初始化对应对象
- 获取结果集的元数据(包含列名,返回的列值信息)
- 结果集获得列值,元数据获得列名
- 通过反射利用列名设置对象的列值
- 注意:此时拿到的是列名而不是属性名,如果列名和属性名不同,需要在select语句中显示改别名为属性名。使用第二个获取别名
getcolunmname()//获取列名
getcolumnlabel()//获取别名
④关闭资源
- preparedstatement
- connection
- resultSet
⑤代码举例
- 得到确定的数据(得到一条查询结果)
@Test
public void testQuery1(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth from customers where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1, 1);
//增删改执行ps.execute无返回,此时需要有返回结果集,调用ps.executeQuery();返回结果集
resultSet = ps.executeQuery();
//处理结果集
if(resultSet.next()){//next作用判断结果集下一条是否发有数据,有数据返回true指针下移,没有数据返回false指针不下移
//获取当前数据的各个字段值
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String email = resultSet.getString(3);
Date birth = resultSet.getDate(4);
//low
//System.out.println("id =" + id);
//也不好
//Object[] data = new Object[](id,name,email,birth);
//数据封装为对象
Customer customer = new Customer(id,name,email,birth);
System.out.println(customer);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//关闭资源
JDBCUtils.closeResource(conn, ps,resultSet);
}
- 不确定的查询语句返回(返回一条结果)
//对customer表的通用查询操作
public static Customer customerEquiry(String sql,Object ...args) throws Exception{
//1.获取数据库的链接
Connection conn = JDBCUtils.getConnection();
//2.预编译sql语句返回preparedstatement实例(已经知道要干什么事儿)
PreparedStatement ps = conn.prepareStatement(sql);
//3.填充sql占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1, args[i]);
}
ResultSet resultSet = ps.executeQuery();
//获取结果集的元数据,通过元数据获取结果集的列数
ResultSetMetaData rsmd = resultSet.getMetaData();
int column = rsmd.getColumnCount();
//4.返回结果集
if(resultSet.next()){//next作用判断结果集下一条是否发有数据,有数据返回true指针下移,没有数据返回false指针不下移
Customer cust = new Customer();
//处理结果集一行数据的每一个列
for(int i = 0;i < column;i++){
//获取列值
Object columnvalue = resultSet.getObject(i+1);
//获取结果集中的每个列的列名
String columnName = rsmd.getColumnName(i+1);
//给cust对象指定的某个属性columnName赋值为columnvalue,通过反射
Field field = Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(cust, columnvalue);
}
return cust;
}
JDBCUtils.closeResource(conn, ps, resultSet);
return null;
}
}
- 不确定的表以及多条返回结果
//实现针对不同表的查询操作(返回表中的一条记录)
//T对应的运行时类的名称
public static <T> T getInstance(Class<T> clazz,String sql,Object ...args) throws Exception{
//1.获取数据库的链接
Connection conn = JDBCUtils.getConnection();
//2.预编译sql语句返回preparedstatement实例(已经知道要干什么事儿)
PreparedStatement ps = conn.prepareStatement(sql);
//3.填充sql占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1, args[i]);
}
ResultSet resultSet = ps.executeQuery();
//获取结果集的元数据,通过元数据获取结果集的列数
ResultSetMetaData rsmd = resultSet.getMetaData();
int column = rsmd.getColumnCount();
//4.返回结果集
if(resultSet.next()){//next作用判断结果集下一条是否发有数据,有数据返回true指针下移,没有数据返回false指针不下移
T t = clazz.newInstance();
//处理结果集一行数据的每一个列
for(int i = 0;i < column;i++){
//获取列值
Object columnvalue = resultSet.getObject(i+1);
//获取结果集中的每个列的列名
String columnName = rsmd.getColumnName(i+1);
//Class clazz1 = T.class;为什么下面不能写T.class
//给t对象指定的某个属性columnName赋值为columnvalue,通过反射
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnvalue);
}
return t;
}
JDBCUtils.closeResource(conn, ps, resultSet);
return null;
}
//针对不同的表返回多条数据
public static <T> List<T> getForList(Class<T> clazz,String sql,Object ...args) throws Exception{
//1.获取数据库的链接
Connection conn = JDBCUtils.getConnection();
//2.预编译sql语句返回preparedstatement实例(已经知道要干什么事儿)
PreparedStatement ps = conn.prepareStatement(sql);
//3.填充sql占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1, args[i]);
}
ResultSet resultSet = ps.executeQuery();
//获取结果集的元数据,通过元数据获取结果集的列数
ResultSetMetaData rsmd = resultSet.getMetaData();
int column = rsmd.getColumnCount();
//集合对象
ArrayList<T> list = new ArrayList<T>();
while(resultSet.next()){
T t = clazz.newInstance();
//处理结果集一行数据的每一个列;给t对象指定的属性赋值
for(int i = 0;i < column;i++){
//获取列值
Object columnvalue = resultSet.getObject(i+1);
//获取结果集中的每个列的列名
String columnName = rsmd.getColumnName(i+1);
//Class clazz1 = T.class;为什么下面不能写T.class
//给t对象指定的某个属性columnName赋值为columnvalue,通过反射
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnvalue);
}
list.add(t);
}
JDBCUtils.closeResource(conn, ps, resultSet);
return list;
}
3.preparedstatement特性(用占位符)
①解决sql注入问题
- preparedstatement预编译sql语句(传入参数前已经确定了关系),然后挖两个坑传入参数
- statement直接用完整sql语句执行