1.预处理的搬运工对象
Connection对象下面的方法 PreparedStatement prepareStatement(String sql) throws SQLException 创建一个PreparedStatement对象,用于将参数化的SQL语句发送到数据库。 具有或不具有IN参数的SQL语句可以预编译并存储在PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。 注意:此方法针对从预编译中受益的参数化SQL语句进行了优化。 如果驱动程序支持预编译,则方法prepareStatement将将该语句发送到数据库进行预编译。 有些驱动程序可能不支持预编译。 在这种情况下,在执行PreparedStatement对象之前,该语句可能不会发送到数据库。 这对用户没有直接影响; 然而,它确实影响了哪些方法抛出某些SQLException对象。静态的SQL语句: insert into work (name, age, info) values("狗蛋", 12, "asjj"); Statement
参数化的SQL语句: insert into work (name, age, info) values(?, ?, ?); PreparedStament
package com.qf.a_mysql; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class Demo1 { public static void main(String[] args) throws Exception{ //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/java2304?useSSL=false"; String user = "root"; String password = "123456"; Connection connection = DriverManager.getConnection(url, user, password); //1.创建预处理的搬运工对象 String sql = "insert into work (name, age, info) values(?, ?, ?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); //2.SQL语句已预编译并存储在PreparedStatement对象中 //对sql语句中的?进行赋值 //使用给定对象设置指定参数的值。 preparedStatement.setObject(1, "狗蛋");//对第一个?进行赋值 preparedStatement.setObject(2, 12);//对第一个?进行赋值 preparedStatement.setObject(3, "喜欢教案");//对第一个?进行赋值 //3.执行sql语句 int i = preparedStatement.executeUpdate(); System.out.println(i); //4.关闭资源 preparedStatement.close(); connection.close(); } }
修改案例
package com.qf.a_mysql; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class Demo2 { public static void main(String[] args) throws Exception{ //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/java2304?useSSL=false"; String user = "root"; String password = "123456"; Connection connection = DriverManager.getConnection(url, user, password); String sql = "update work set name = ?, age = ?, info = ? where id = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1, "寅豪"); preparedStatement.setObject(2, 39); preparedStatement.setObject(3, "是个好人"); preparedStatement.setObject(4, 6); int i = preparedStatement.executeUpdate(); System.out.println(i); preparedStatement.close(); connection.close(); } }
2.封装一个工具类
做几件事情?
1.获取connection对象
2.关闭资源
package com.qf.utils; import java.sql.*; /** * 1.获取Connection对象 * 2.关闭资源 */ public class JdbcUtil { private static String url = "jdbc:mysql://localhost:3306/java2304?useSSL=false"; private static String user = "root"; private static String password = "123456"; static { //只要JdbcUtil类加载了,就会执行静态代码块中的代码 try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection () { Connection connection = null; try { connection = DriverManager.getConnection(user, url, password); } catch (SQLException e) { e.printStackTrace(); } return connection; } //关闭资源 //增删改 需要关闭两个 查 关闭三个 public static void close (Connection connection) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } public static void close (Statement statement, Connection connection) { try { statement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } public static void close (ResultSet resultSet, Statement statement, Connection connection) { try { resultSet.close(); statement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } }
上面封装的类好不好?不好的 封装好以后不能再修改了,这才叫封装。
数据库变了,密码变了 都得改!!!
去将连接库的一些信息参数 放到配置文件中,然后让Java去读取配置文件。改的时候只需要改配置文件
按照步骤:
1.在src文件夹下面新建一个叫 db.properties 2.在 db.properties 去写连接数据库的参数 以键值对的数据 驱动 url user password
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/java2304?useSSL=false user=root password=123456
package com.qf.utils; import java.io.FileInputStream; import java.sql.*; import java.util.Properties; /** * 1.获取Connection对象 * 2.关闭资源 */ public class JdbcUtil { private static String url = null; private static String user =null; private static String password = null; static { try { //只要JdbcUtil类加载了,就会执行静态代码块中的代码 //读取配置文件中的信息:properties文件 //Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 // 属性列表中的每个键及其对应的值都是一个字符串。 Properties properties = new Properties(); properties.load(new FileInputStream("src/db.properties")); //数据都在properties 对象中 String diver = properties.getProperty("driver"); url = properties.getProperty("url"); user = properties.getProperty("user"); password = properties.getProperty("password"); Class.forName(diver); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () { Connection connection = null; try { connection = DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); } return connection; } //关闭资源 //增删改 需要关闭两个 查 关闭三个 public static void close (Connection connection) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } public static void close (Statement statement, Connection connection) { try { statement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } public static void close (ResultSet resultSet, Statement statement, Connection connection) { try { resultSet.close(); statement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } }
3.SQL注入【了解】
在使用SQL的时候,会有漏洞。是sql机制问题
delete from emp where id = 6 or 1=1 一旦or 1=1 条件就没有用了
搬运工对象Statement 是无法防止这个SQL注入的
预处理搬运工对象 是可以防止SQL注入
package com.qf.c_mysql; import com.qf.utils.JdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; public class Demo1 { public static void main(String[] args) throws Exception { preparestatementTest(); } public static void statementTest () throws Exception { Connection connection = JdbcUtil.getConnection(); Statement statement = connection.createStatement(); String sql = "delete from person where id = 1 or 1=1"; int i = statement.executeUpdate(sql); System.out.println(i); JdbcUtil.close(statement, connection); } public static void preparestatementTest () throws Exception { Connection connection = JdbcUtil.getConnection(); String sql = "delete from user where id = ? "; //delete from user where id = 1 or 1=1 PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1, "1"); int i = preparedStatement.executeUpdate(); System.out.println(i); JdbcUtil.close(preparedStatement, connection); } }
4.JavaBean规范
JavaBean 是实体类 Work类 写实体的类的时候 一定要规范
1.私有化成员变量
2.一定要提供一个无参构造方法
3.一定要提供set和get方法
4.每个类都要单独成为一个文件
package com.qf.c_mysql; public class Work { private Integer id; private String name; private Integer age; private String info; public Work() { } public Work(Integer id, String name, Integer age, String info) { this.id = id; this.name = name; this.age = age; this.info = info; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }
5.BeanUtils 类
BeanUtils jdk官方手册搜的时候没有,就说明 是第三方提供的一个类
BeanUtils类 是动态的对属性进行赋值和取值的,之前使用set方法和get方法对属性进行赋值和取值
现在可以BeanUtils
1.下载两个 beanutils和logging jar包
package com.qf.d_beanutils; import com.qf.c_mysql.Work; import org.apache.commons.beanutils.BeanUtils; import java.lang.reflect.InvocationTargetException; public class Demo1 { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { //setProperty 设置属性 //第一个参数: Object bean 属性所在的类对象 //第二个参数: String name 属性的名字 //第三个参数: Object value 属性的值 //static void setProperty(Object bean, String name, Object value) Work work = new Work(); BeanUtils.setProperty(work, "name", "大黄"); //static String getProperty(Object bean, String name) String name = BeanUtils.getProperty(work, "name"); System.out.println(name); } }
6.元数据【重点】
metadata
关于sql参数的元数据
关于resultSet元数据
6.1关于sql参数的元数据
package com.qf.e_metadata; import com.qf.utils.JdbcUtil; import java.sql.Connection; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.SQLException; public class Demo1 { public static void main(String[] args) throws SQLException { Connection connection = JdbcUtil.getConnection(); String sql = "insert into work (name, age, info) values(?,?,?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); //获取sql的参数(?)有几个 借助于参数元数据 //借助于U预处理搬运工对象获取 参数元数据对象 ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData(); int parameterCount = parameterMetaData.getParameterCount();//得到参数的个数 System.out.println(parameterCount);//3 //要个数有啥用 Object[] objs = {"狗蛋", 12, "嘻嘻按时间计算"};//数组 用来对?进行赋值的 for (int i = 1; i <= parameterCount; i++) { /** * preparedStatement.setObject(1, "狗蛋"); * preparedStatement.setObject(2, 12); * preparedStatement.setObject(3, “嘻嘻按时间计算”); */ //对?进行赋值 preparedStatement.setObject(i, objs[i - 1]); } } }
6.2关于结果集的元数据
package com.qf.e_metadata; import com.qf.utils.JdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; public class Demo3 { public static void main(String[] args) throws Exception{ Connection connection = JdbcUtil.getConnection(); String sql = "select * from work"; PreparedStatement preparedStatement = connection.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery(); //通过结果集对象获取结果集元数据 ResultSetMetaData metaData = resultSet.getMetaData(); //ResultSetMetaData 结果集元数据中存的有字段的个数 和字段名字 等 /** * id name age info * 1 狗蛋 12 才能决定书 * 2 老邢 89 挖掘机 */ //getColumnCount 字段的个数 int columnCount = metaData.getColumnCount(); System.out.println(columnCount); //获取字段的名字 // String id = metaData.getColumnName(1); // System.out.println(id); // String name = metaData.getColumnName(2); // System.out.println(name); // String age = metaData.getColumnName(3); // System.out.println(age); // String info = metaData.getColumnName(4); // System.out.println(info); for (int i = 1; i <= columnCount ; i++) { String columnName = metaData.getColumnName(i); System.out.println(columnName); } //要字段的名字干嘛? //今天我要看到 这个双层循环执行流程 /** * id name age info * 1 狗蛋 12 才能决定书 * 2 老邢 89 挖掘机 ===> * 最外层循环进来 * 进入for循环 * i=1 1<=4 getColumnName(1); => id getObject(id); 1 i++ * i=2 2<=4 getColumnName(2); =->name getObject(name);狗蛋 i++ * i=3 * i=4 * 再次回到外层循环 * 进入for循环 * i=1 1<=4 getColumnName(1); id getObject(id); 2 */ while (resultSet.next()) {//行 for (int i = 1; i <= columnCount ; i++) {//列 String columnName = metaData.getColumnName(i); Object object = resultSet.getObject(columnName); System.out.println(object); } } } }