JDBC
一、JDBC
1.定义
Java Database Connection,Java数据库连接技术
2.JDBC API
java提供的接口,位于java.sql包
-
Driver
用于连接数据库的接口,数据库厂商提供的JDBC工具包必须包含该接口的实现
//根据给定URL地址连接数据库 //info提供数据库账号密码 Connection connect(String url, java.util.Properties info) throws SQLException;
-
DriverManager
数据库厂商提供的JDBC交给DriverManager管理,负责获取数据库连接对象
String url = "jdbc:mysql://localhost:3306/数据库名称?serverTimezone=Asia/Shanghai"; public static Connection getConnection(String url, String user, String password) throws SQLException;
-
Connection
提供与数据库交互的功能接口
//创建一个SQL语句执行对象 Statement createStatement() throws SQLException; //创建一个预处理SQL执行对象 PreparedStatement prepareStatement(String sql) throws SQLException; //创建一个存储过程SQL执行对象 CallableStatement prepareCall(String sql) throws SQLException; //设置该连接上所有操作是否执行自动提交 void setAutoCommit(boolean autoCommit) throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; //设置事务隔离级别 void setTransactionIsolation(int level) throws SQLException;
//不支持事物 int TRANSACTION_NONE = 0; //读取未提交的数据 int TRANSACTION_READ_UNCOMMITTED = 1; //读取已提交的数据 int TRANSACTION_READ_COMMITTED = 2; //可重复读 int TRANSACTION_REPEATABLE_READ = 4; //串行化 int TRANSACTION_SERIALIZABLE = 8;
-
Statement
执行SQL语句接口
//执行查询,得到结果集 ResultSet executeQuery(String sq) throws SQLException; //执行更新,得到受影响的行数 int executeUpdate(String sql) throws SQLException; //关闭SQL语句执行器 void close() throws SQLException; //将SQL语句添加到批处理执行SQL列表中 void addBatch(String sql) throws SQLException; //执行批处理,返回每条SQL语句的执行结果 int[] executeBatch() throws SQLException;
-
ResultSet
查询结果集接口
//光标向下移动一行,默认为第0行,若有数据,则返回true,否则返回false boolean next() throws SQLException; void close() throws SQLException; //获取指定列的字符串值 //也可以传列的名字 String getString(int columnIndex) throws SQLException; boolean getBoolean(int columnIndex) throws SQLException; int getInt(int columnIndex) throws SQLException; //对象同时转换为type Object getObject(int columnIndex, Class type) throws SQLException; //获取结果集元数据:列名称,列数量,列别名等 ResultSetMetaData getMetaData() throws SQLException; //光标向上移动一行 boolean previous() throws SQLException;
3.JDBC操作步骤
-
引入驱动包
将mysql-connector-java.jar引入工程
-
加载驱动
//MySQL5.0 Class.forName("com.mysql.jdbc.Driver"); //MySQL8.0 Class.forName("com.mysql.cj.jdbc.Driver");
-
获取连接Connection
-
创建SQL语句执行器Statement
-
使用执行器执行查询得到结果集ResultSet
-
通过结果集获取数据(利用循环)
-
释放资源:3个close
4.预处理SQL
-
SQL注入
实例:用户查询输入条件1=‘1’,此时查询所有信息,使查询结果发生改变
-
PreparedStatement常用方法
//执行查询,得到一个结果集 ResultSet executeQuery() throws SQLException; //执行更新,得到受影响的行数 int executeUpdate() throws SQLException; //使用给定的整数值设置给定位置的参数 void setInt(int parameterIndex, int x) throws SQLException; //使用给定的长整数值设置给定位置的参数 void setLong(int parameterIndex, long x) throws SQLException; //使用给定的双精度浮点数值设置给定位置的参数 void setDouble(int parameterIndex, double x) throws SQLException; //使用给定的字符串值设置给定位置的参数 void setString(int parameterIndex, String x) throws SQLException; //使用给定的对象设置给定位置的参数 void setObject(int parameterIndex, Object x) throws SQLException; //获取结果集元数据 ResultSetMetaData getMetaData() throws SQLException;
-
在sql语句中使用?占位符来替代用户输入的内容,在利用set设置占位符内容
这样,就会把用户输入的内容看作一个整体,用户就不能通过输入类似sql语句的内容进行sql注入
-
在实际运用中,推荐使用预处理执行器,确保数据库安全
-
经典实例:
public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入商品名称:"); String goodsName = sc.nextLine(); String url = "jdbc:mysql://localhost:3306/lesosn?serverTimezone=Asia/Shanghai"; String username = "root"; String password = "root"; try { Class.forName("com.mysql.cj.jdbc.Driver"); Connection con = DriverManager.getConnection(url,username,password); String sql = "SELECT id,name,number,price,agent_id FROM goods WHERE name= ? LIMIT 0,20"; PreparedStatement ps = con.prepareStatement(sql); //设置问号替换的值 ps.setString(1, goodsName); ResultSet rs = ps.executeQuery(); while(rs.next()){ long id = rs.getLong("id"); String name = rs.getString("name"); int number = rs.getInt("number"); double price = rs.getDouble("price"); int agent_id = rs.getInt("agent_id"); System.out.println(id + "," + name + "," + number + "," + price + "," + agent_id); } rs.close(); ps.close(); con.close(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (SQLException e) { throw new RuntimeException(e); } }
二、反射
1.Class类
-
Java程序通过编译器编译生成class文件,class文件在JVM上执行
-
定义一个类来描述所有类的共同特征:
public class Class {
private String name; //类名
private Package pk; //包名
private Constructor[] constructors; //构造方法
private Field[] fields; //字段
private Method[] methods; //方法
private Class<?>[] interfaces; //实现的接口
private Class<?> superClass; //继承的父类
//省略getter/setter
}
-
JVM加载类时对类进行解析,将类信息存储进Class对象中。
在使用的过程中,JVM通过Class对象反推到我们编写的类的内容,这个过程为反射。
-
java提供类:java.lang.Class
-
获取类对应的Class对象
Class<类名> clazz = 类名.class; Class<类名> clazz = 对象名.getClass(); Class<类名> clazz = Class对象名.getSuperClass(); Class clazz = Class.forName("类的全限定名"); //包名.类名 Class<类名> clazz = 包装类.TYPE;
-
Class常用方法
//获取类中使用public修饰的字段 public Field[] getFields() throws SecurityException; //获取类中定义的所有字段 public Field[] getDeclaredFields() throws SecurityException; //通过给定字段名获取类中定义的字段,不能是私有的 public Field getField(String name) throws NoSuchFieldException, SecurityException; //获取类中使用public修饰的方法 public Method[] getMethods() throws SecurityException; public Method[] getDeclaredMethods() throws SecurityException; public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchFieldException, SecurityException; public Constructor[]<类名> getConstructors() throws SecurityException; public Constructor[]<类名> getDeclaredConstructors() throws SecurityException; public Constructor<类名> getConstructor(Class<?>... parameterTypes) throws NoSuchFieldException,SecurityException; //获取类的全限定名 public String getName(); //获取类名 public String getSimpleName(); //获取类所在的包 public Package getPackage(); //判断该类是否是基本数据类型 public native boolean isPrimitive(); public native boolean isInterface(); public native boolean isArray(); //通过类的无参构造创造一个实例,确保无参构造是公开的 public T newInstance() throws InstantiationException, IllegalAccessException;
-
反射实例:利用Class创建一个学生对象,并为name字段赋值,并反推出name的get和set方法
public static void main(String[] args){
Class<Student> clazz = Student.class;
Constructor<? extends Student> c = clazz.getDeclaredConstructor();
//Student类中的无参构造是私有的
c.setAccessible(ture);
Student s = clazz.newInstance();
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
//给指定对象的字段赋值
nameField.set(s, "李四");
//反推方法getName
String fieldName = nameField.getName();
String methodName = "get" + fieldName.subString(0,1).toUpperCase() + fieldName.subString(1);
Method m = clazz.getDeclaredMethod(methodName);
m.setAccessible(true);
String name = (String) m.invoke(s);
System.out.println(name);
//反推方法setName
methodName = "set" + fieldName.subString(0,1).toUpperCase() + fieldName.subString(1);
m = clazz.getDeclaredMethod(methodName, nameField.getType());
m.setAccessible(true);
m.invoke(s, "李刚");
}
//省略抛出异常
2.反射与数据库
如果查询结果的字段与对象的属性名保持一致,就可以利用反射完成万能查询。
万能查询实例:
public class JdbcUtil {
private static final String url = "jdbc:mysql://localhost:3306/lesosn?serverTimezone=Asia/Shanghai";
private static final String username = "root";
private static final String password = "root";
static{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("驱动加载异常");
}
}
//test
public static void main(String[] args) {
String sql = "SELECT id,name,region_id AS regionId FROM agent WHERE name LIKE ?";
Object[] params = {"%魅%"};
List<Agent> agentList = query(sql, Agent.class, params);
agentList.forEach(System.out::println);
}
public static <T> List<T> query(String sql, Class<T> clazz, Object...params){
List<T> list = new ArrayList<>();
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = DriverManager.getConnection(url, username, password);
ps = createPreparedStatement(connection, sql, params);
rs = ps.executeQuery();
while(rs.next()){
T t = createInstance(clazz, rs);
list.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(rs, ps, connection);
}
return list;
}
public static int update(String sql, Object...params){
int result = 0;
Connection connection = null;
PreparedStatement ps = null;
try {
connection = DriverManager.getConnection(url, username, password);
ps = createPreparedStatement(connection, sql, params);
result = ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
close(ps,connection);
}
return result;
}
//利用反射,构造对象
private static <T> T createInstance(Class<T> clazz, ResultSet rs)
throws NoSuchMethodException, InvocationTargetException, InstantiationException,IllegalAccessException {
Constructor<? extends T> c = clazz.getConstructor();
c.setAccessible(true);
T t = c.newInstance();
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
String fieldName = field.getName();
String methodName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
Method m = clazz.getDeclaredMethod(methodName, field.getType());
//会产生找不到这个字段的问题
try{
Object value = rs.getObject(fieldName, field.getType());
m.invoke(t, value);
}catch (Exception e){
continue;
}
}
return t;
}
private static PreparedStatement createPreparedStatement(Connection connection, String sql, Object...params) throws SQLException {
PreparedStatement ps = connection.prepareStatement(sql);
if(params != null && params.length > 0){
for(int i = 0; i < params.length; ++i){
ps.setObject(i + 1, params[i]);
}
}
return ps;
}
private static void close(AutoCloseable...closeable){
if(closeable != null && closeable.length > 0){
for(AutoCloseable ac : closeable){
if(ac != null){
try {
ac.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
.