前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java反射机制和JDBC

Java反射机制和JDBC

作者头像
星辰xc
发布2022-04-09 13:50:58
3860
发布2022-04-09 13:50:58
举报
文章被收录于专栏:星辰大海byXC星辰大海byXC

Java反射机制

1、反射机制有什么用?

代码语言:javascript
复制
通过java语言中的反射机制可以操作字节码文件

可以读和写字节码文件

通过反射机制可以操作代码片段(class文件)

2、反射机制相关类在哪个包下

代码语言:javascript
复制
java.lang.reflect.*;

3、反射机制相关的重要类有哪些?

代码语言:javascript
复制
java.lang.Class:代表整个字节码,代表一个类型,整个类

java.lang.reflect.Method:代表字节码中方法字节码,类中方法

java.lang.reflect.Constructor:代表字节码中构造方法字节码,类中构造方法

java.lang.reflect.Field:代表字节码中属性字节码。类中属性

三种获取class方法:

  1. 第一种
代码语言:javascript
复制
/*
Class.forName()
  1.静态方法
  2.参数是一个字符串,且是一个完整的类名
  3.完整名必须带有包名。java.lang包不能省略
  4.需要处理异常
*/
try{
  Class c1 = Class.forName("java,lang.String");	//获取string类型的class字节码
  Class c2 = Class.forName("java,util.Date");
  Class c3 = Class.forName("java,lang.Integer");
  Class c4 = Class.forName("java,lang.System");
} catch (ClassNotFoundException e) {
  e.printStackTrace;
}

2.第二种

代码语言:javascript
复制
// Java中任何一个对象都有一个getClass方法
String s = "abc";
Class x = s.getClass(); // x代表String.class

Date time = new Date();
Class y = time.getClass();

3.第三种

代码语言:javascript
复制
// java中任何一个类型,包括基本数据类型,都有class属性
Class z = String.class; // z 代表String类型
Class e = int.class;  // e 代表int类型
Class b = Date.class; // b 代表Date类型

获取到class能干什么?

1.可以实例化对象

代码语言:javascript
复制
try {
  // 通过反射机制,获取class,可以实例化对象
  Class c = Class.forName("re.User"); // c代表user类型
  
  // newInstance() 这个方法会调用User这个类的无参构造方法,完成对象的创建
  Object obj = c.newInstance();
  
  System.out.println(obj);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
  e.printStackTrace();
}

2.实例化对象很灵活

代码语言:javascript
复制
public static void main(String[] args) throws Exception {
  // 通过io流读取classinfo.properties这个配置文件
  FileReader fr = new FileReader("classinfo.properties");
  
  /*
    另一种写法,以流的形式返回
    InputStream fr = Thread.currentThread()
    .getContextClassLoader().getResourceAsStream("classinfo.properties");
  */
  
  // 创建属性类对象map
  Properties pro = new Properties();
  
  // 加载
  pro.load(fr);
  
  // 关闭流
  fr.close();
  
  // 通过key获取value
  String className = pro.getProperty("className");
  
  // 通过反射机制实例化对象
  Class c = Class.forName(className);
  
  // 实例化对象
  Object obj = c.newInstance();
  System.out.println(obj);
}

如果只希望一个类的静态代码块执行,其他代码一律不执行,就可以使用以下方式

代码语言:javascript
复制
public class TestDemo{
  public static void main(String[] args) {
    try {
      // Class.forName()这个方法执行会导致类加载
      // 类加载时,静态代码块执行 
      Class.forName("re.Myclass");
    } catch (ClassNotFoundException) {
      e.printStackTrace();
    }
  }
}
class Myclass {
  // 静态代码块在类加载时执行,并且只执行一次
  static {
    System.out.println("MyClass类的静态代码块执行了!");
  }
}

获取类路径下文件的绝对路径

代码语言:javascript
复制
public static void main(String[] args) {
  // 前提是此文件必须在src类路径下,才能获取到,起点是src
  // 注意获取java文件时需要加.class
  /*
    解释:Thread.currentThread()获取当前线程对象。
          getContextClassLoader()是线程对象的方法,
          可以获取到当前线程的类加载器对象。
          getResource()【获取资源】这是类加载器对象的方法,
          当前线程的类加载器默认从类的根路径下加载资源。
  */
  String path = Thread.currentThread().getContextClassLoader()
          .getResource("classinfo.properties").getPath();
          // 起点是src如:re/User.class
          // 这种方式获取绝对路径是通用的
  System.out.println(path);
}

资源绑定器

代码语言:javascript
复制
/*
java.util包下提供了一个资源绑定器,便于获取属性配置文件的内容
使用以下方式的时候,属性配饰文件必须是xxx.properties,且必须放到类路径下
*/
public static void main(String[] args) {
  // 资源绑定器,这个文件必须是配置文件properties,且在类路径下,参数不能有后缀名
  ResourceBundle rb = ResourceBundle.getBundle("classinfo");

  String name = rb.getString("className");
  System.out.println(name);
}

反射属性

代码语言:javascript
复制
/*测试类*/
package re;
public class Demo {
    public String name;
    private int age;
    protected String sex;
    double moeny;
    public static final float PI = 3.14f;
}

package re;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*反射属性测试类*/
public class FieldTest {
    public static void main(String[] args) throws Exception {
        // 先获取整个类
        Class c = Class.forName("re.Demo");

        String className = c.getName();
        System.out.println("完整类名:" + className);

        String simpleName = c.getSimpleName();
        System.out.println("简类名:" + simpleName + "\n");

        // getFields()此方法,获取类中所有公开属性
        Field[] f = c.getFields();

        // 获取数组中第0位元素的名字
        System.out.println(f[0].getName() + "\n");

        // 获取类中所有属性的名字,getDeclaredFields()此方法返回Field数组
        Field[] fs = c.getDeclaredFields();
        for (Field field : fs) {
            // 获取修饰符,getModifiers()返回一个整型,每一个数字对应一个修饰符代号
            int i = field.getModifiers();
            System.out.print("修饰符代号:" + i + "\t");
            // 可以将这一个修饰符代号转换为字符串
            System.out.print("对应修饰符名称:" + Modifier.toString(i) + "\t");
            // 获取属性类型,getType()方法返回Class
            Class fieldtype = field.getType();
            // 通过Class可以调用getName,打印属性类型名称
            System.out.print("完整类型名:" + fieldtype.getName() + "\t");
            System.out.print("简单类型名:" + fieldtype.getSimpleName() + "\t");
            // 获取属性名字
            System.out.println("属性名:" + field.getName());
        }
    }
}

小案例:反编译类中属性

代码语言:javascript
复制
/*测试类*/
package re;
public class Demo {
    public String name;
    private int age;
    protected String sex;
    double moeny;
    public static final float PI = 3.14f;
}

package re;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*反编译测试类中属性*/
public class FieldTest01 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("re.Demo");
        StringBuilder s = new StringBuilder();
        s.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n");
        Field[] fs = c.getDeclaredFields();
        for (Field f : fs) {
            s.append("\t" + Modifier.toString(c.getModifiers()) + " " + f.getType().getSimpleName() + " " + f.getName() + ";" + "\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

获取和设置属性值

代码语言:javascript
复制
public class Demo {
    public String name;
    private int age;
    protected String sex;
    double moeny;
    public static final float PI = 3.14f;
}
// 重点
public static void main(String[] args) throws Exception {
    // 通过反射机制给属性赋值和调佣(set和get)
    Class c = Class.forName("Demo");
    // 创建对象,底层调用了re.Demo类中无参构造方法实现的
    Object obj = c.newInstance();   // obj为Demo对象
    // getDeclaredField()方法给定参数(属性名),返回一个指定Field
    Field f = c.getDeclaredField("name");
    // get()方法给定参数(对象)获取Field的值,属性.get(对象)
    System.out.println(f.get(obj));
    f.set(obj, "张三");   // 设置Demo对象中name属性的值
    System.out.println(f.get(obj));
    // 设置私有属性的值
    Field ageField = c.getDeclaredField("age");
    // 打破封装,这种方式会使得私有属性在外部也能访问
    ageField.setAccessible(true);
    ageField.set(obj, 18);
    System.out.println(ageField.get(obj));
}

可变长参数

代码语言:javascript
复制
// 语法:参数为数据类型...参数名
// 1,可变参数要求的是参数0~n个
// 2,最后一个,仅一个
// 例;
public static void main(String[] args) {
    a();
    a(23,4);
    a(1);
    b("我","是","中","国","人");
}
public static void a(int...age) {
    System.out.println("a方法执行了!");
}
public static void b(String...value) {
    // 可变长参数可以作为数组使用
    for (String s : value) {
        System.out.println(s);
    }
}

反射方法

代码语言:javascript
复制
package re;
public class User {
    public boolean login(String name, String pwd) {
        if ("xc".equals(name) && "123456".equals(pwd)) {
            return true;
        }
        return false;
    }
    public void out() {
        System.out.println("系统退出,谢谢使用!");
    }
}

package re;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class MethodTest {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("re.User");
        // 获取所有的方法(包括私有)
        Method[] me = c.getDeclaredMethods();
        for (Method method : me) {
            // 获取访问修饰符
            System.out.print(Modifier.toString(method.getModifiers()) + "\t");
            // 获取返回值类型
            System.out.print(method.getReturnType().getSimpleName() + "\t");
            // 获取方法名
            System.out.println(method.getName());
            // 获取方法参数数据类型列表
            Class[] type = method.getParameterTypes();
            for (Class aClass : type) {
                System.out.println(aClass.getSimpleName());
            }
        }
    }
}

反编译方法签名

代码语言:javascript
复制
package re;
public class User {
    public boolean login(String name, String pwd) {
        if ("xc".equals(name) && "123456".equals(pwd)) {
            return true;
        }
        return false;
    }
    public void out() {
        System.out.println("系统退出,谢谢使用!");
    }
}


package re;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Demo2 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("re.User");
        StringBuilder sb = new StringBuilder();
        sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName()+ " {\n");
        Method[] md = c.getDeclaredMethods();
        for (Method method : md) {
            sb.append("\t");
            sb.append(Modifier.toString(method.getModifiers()));
            sb.append(" ");
            sb.append(method.getReturnType().getSimpleName());
            sb.append(" ");
            sb.append(method.getName());
            sb.append("(");
            Class[] cs = method.getParameterTypes();
            for (Class aClass : cs) {
                sb.append(aClass.getSimpleName() + " ,");
            }
            if (cs.length != 0) {
              // 删除指定下标上的字符
              sb.deleteCharAt(sb.length() - 1);
            }
            sb.append(")");
            sb.append("\n");
        }
        sb.append("}");
        System.out.println(sb);
    }
}

通过反射机制调用方法

代码语言:javascript
复制
package re;
import java.lang.reflect.Method;
// 重点
public class MethodTest2 {
    public static void main(String[] args) throws Exception {
        // 不使用反射机制调用方法
        // 创建对象
        User user = new User();
        // 调用方法
        boolean flag = user.login("xc", "123456");
        System.out.println(flag ? "登陆成功" : "登录失败");

        // 使用反射机制调用方法
        Class c = Class.forName("re.User");
        Object obj = c.newInstance();
        // 获取Method
        Method loginMethod = c.getDeclaredMethod("login", String.class, String.class);
        // 调用方法
        Object retrunValue = loginMethod.invoke(obj, "xc", "123");
        // 强制转换类型
        boolean flag2 = (Boolean) retrunValue;
        System.out.println(flag2 ? "登陆成功" : "登录失败");
    }
}

反编译构造方法

代码语言:javascript
复制
package re;
/*测试类*/
public class Vip {
    int no;
    int age;
    String name;
    String birthday;

    public Vip(int no) {
        this.no = no;
    }

    public Vip(int no, int age) {
        this.no = no;
        this.age = age;
    }

    public Vip(int no, int age, String name) {
        this.no = no;
        this.age = age;
        this.name = name;
    }

    public Vip(int no, int age, String name, String birthday) {
        this.no = no;
        this.age = age;
        this.name = name;
        this.birthday = birthday;
    }
}



package re;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*反编译构造方法*/
public class ConstructorTest {
    public static void main(String[] args) throws Exception {
        // 创建StringBuilder
        StringBuilder sb = new StringBuilder();
        // 获取Class
        Class c = Class.forName("re.Vip");
        // 获取修饰符列表
        sb.append(Modifier.toString(c.getModifiers()));
        sb.append(" class ");
        // 获取简类名
        sb.append(c.getSimpleName());
        sb.append(" {\n");
        // 获取属性(数组)
        Field[] fd = c.getDeclaredFields();
        for (Field field : fd) {
          // 通过for循环遍历属性数组,获取属性数据类型,属性名
            sb.append("\t" + field.getType().getSimpleName() + " " + field.getName() + ";\n");
        }
        sb.append("\n");
        // 获取构造方法(数组)
        Constructor[] cons = c.getDeclaredConstructors();
        for (Constructor constructor : cons) {
            sb.append("\t");
            // 遍历数组获取修饰符列表,简答类名
            sb.append(Modifier.toString(c.getModifiers()));
            sb.append(" " + c.getSimpleName() + "(");
            // 获取参数数据类型列表
            Class[] par = constructor.getParameterTypes();
            for (Class aClass : par) {
                // 获取参数数据类型简名
                sb.append(aClass.getSimpleName() + ",");
            }
            // 判断如果数组的长度不为零,就删除逗号
            if (par.length != 0) {
                sb.deleteCharAt(sb.length() - 1);
            }
            sb.append(") {}\n");
        }
        sb.append("}");
        // 打印输出
        System.out.println(sb);
    }}
}

调用构造方法

代码语言:javascript
复制
package re;
/*测试类*/
public class Vip {
    int no;
    int age;
    String name;
    String birthday;

    public Vip(){}

    public Vip(int no) {
        this.no = no;
    }

    public Vip(int no, int age) {
        this.no = no;
        this.age = age;
    }

    public Vip(int no, int age, String name) {
        this.no = no;
        this.age = age;
        this.name = name;
    }

    public Vip(int no, int age, String name, String birthday) {
        this.no = no;
        this.age = age;
        this.name = name;
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Vip{" +
                "no=" + no +
                ", age=" + age +
                ", name='" + name + '\'' +
                ", birthday='" + birthday + '\'' +
                '}';
    }
}


package re;

import java.lang.reflect.Constructor;
/*调用构造方法实例化对象*/
public class ConstructorTest1 {
    public static void main(String[] args) throws Exception {
        // 不使用反射机制调用构造方法
        Vip vip = new Vip();
        Vip vip1 = new Vip(110,24,"xc", "2002-10-15");
        System.out.println(vip1);

        // 使用反射机制调用构造方法
        Class c = Class.forName("re.Vip");
        // 调用无参数构造方法
        Object obj = c.newInstance();
        System.out.println(obj);
        // 调用有参数构造方法
        // 第一步,获取要调用的构造方法
        Constructor con = c.getDeclaredConstructor(int.class, int.class, String.class, String.class);
        // 调用有参构造方法,创建对象
        Object newobj = con.newInstance(110,24,"xc", "2002-10-15");
        System.out.println(newobj);
        
        // 调用无参数构造方法
        Constructor con2 = c.getDeclaredConstructor();
        // 调用无参构造方法,创建对象
        Object newobj2 = con.newInstance();
        System.out.println(newobj2);
    }
}

获取父类和父接口

代码语言:javascript
复制
public static void main(String[] args) throws Exception {
    // 获取父类和父接口
    // String举例
    Class c = Class.forName("java.lang.String");

    // 获取String的父类
    Class supers = c.getSuperclass();
    System.out.println(supers.getName());

    // 获取接口
    Class[] interfaces = c.getInterfaces();
    for (Class in : interfaces) {
        System.out.println(in.getName());
    }
}

注解

注解,也称作注释,英文单词Annotation

注解也是一种引用数据类型,编译之后也会生成xxx.class文件

怎么自定义注解?语法格式?

注解使用场景

jdk内置了哪些注解

@Override标志性注解

元注解

Deprecated,已过时,在调用的时候会出现横线,用于向其他程序员传递一个信息

注解中定义属性

代码语言:javascript
复制
package at;
/*
自定义注解:MyAnnotation
* */
public @interface MyAnnotation {
    /*
    * 通常在注解中可以定义属性,以下为MyAnnotation中的内部属性
    * 非常像方法
    * */
    String name();
    
    // 给属性指定默认值
    int age() default 18;
    
    // 邮箱属性
    String[] emil();
}

package at;
public class AnnotationTest {
    // 在使用时,如果一个注解中有属性,必须给属性赋值
    // 除非有默认值,则可以不写
    // 属性数组中只有一个元素时可以省略大括号
    @MyAnnotation(name = "zhangsan", emil = "fx@outlook.com")
    public AnnotationTest() {}
    // 属性数组有多个元素,则不能省略大括号
    @MyAnnotation(name = "zhangsan", emil = {"fx@outlook.com", "45@123.com"})
    public AnnotationTest() {}
}

关于属性名为value时省略问题:

如果一个注解中只有一个属性名为value,那么可以省略,如果有多个属性,则不能省略

注解中的属性类型可以是: byte,short,int,long。float,double。boolean,char,String,Class,枚举类型 以及上述每一种的数组形式

反射注解

获取类上的注解

代码语言:javascript
复制
package at;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/*
自定义注解:MyAnnotation
* */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    /*
    * 通常在注解中可以定义属性,以下为MyAnnotation中的内部属性
    * 非常像方法
    * */
    String name();

    // 给属性指定默认值
    int age() default 18;

    String value();
}


package at;
/*注解*/
@MyAnnotation(name = "lisi", value = "12")
public class AnnotationTest {
    // 在使用时,如果一个注解中有属性,必须给属性赋值
    @MyAnnotation(name = "zhangsan", value = "12")
    public AnnotationTest() {}
}

package at;
/*通过反射机制获取到注解的值*/
public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("at.AnnotationTest");
        // 判断这个类中是否有MyAnnotation注解
        if (c.isAnnotationPresent(MyAnnotation.class)) {
            // 如果有则可以获取到注解对象
            MyAnnotation m = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
            // 获取到注解对象则可以获取到值,如果不是value则可以点到其他属性
            String name = m.value();
            System.out.println(name);

            String name1 = m.name();
            System.out.println(name1);
        }
    }
}

获取方法上的注解

代码语言:javascript
复制
package at;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ma {
    String user();
    String password();
}


package at;
import java.lang.reflect.Method;
public class Test1 {
    @Ma(user = "xc",password = "123")
    public void todo() {}

    public static void main(String[] args) throws Exception {
        Class c = Class.forName("at.Test1");
        Method m = c.getDeclaredMethod("todo");
        if (m.isAnnotationPresent(Ma.class)) {
            Ma ma = m.getAnnotation(Ma.class);
            System.out.println(ma.user());
            System.out.println(ma.password());
        }
    }
}

JDBC

使用Java链接数据库六步曲(非常重要)

  1. 注册驱动(作用:告诉Java程序,即将要连接的数据库是哪个品牌的数据库)
  2. 获取连接(表示JVM的进程和数据库进程之间的通道打开了,属于进程之间的通信,使用后要关闭对象)
  3. 获取数据库操作对象(专门执行SQL语句的对象)
  4. 执行SQL语句(DQL,DML)
  5. 处理查询结果集(只有当第四步执行的是select的语句的时候,才有第五步的查询结果集)
  6. 释放资源(使用完资源之后一定要关闭资源)
代码语言:javascript
复制
package day1;

import com.mysql.cj.jdbc.Driver;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/*
* JDBC编程六部曲
* */
public class JdbcTest01 {
    public static void main(String[] args) {
        Statement smt = null;
        Connection connection = null;
        try {
            // 1.注册驱动
            DriverManager.registerDriver(new Driver());

            // 2.获取连接
            // 加在库名后面“?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false”
            String url = "jdbc:mysql://localhost:3306/testdb";
            String user = "root";
            String password = "xcfx";
            connection = DriverManager.getConnection(url,user,password);
            // com.mysql.cj.jdbc.ConnectionImpl@34a3d150
            System.out.println("数据库对象:" + connection);

            // 3.获取数据库操作对象(Statement专门执行SQL语句的)
            smt =  connection.createStatement();

            // 4.执行SQL语句
            String sql2 = "update employee set Salary = 5000 where Name = '张三'";
            String sql = "insert into employee values(default,'女','黑马',2000,'2000-5-3',1)";

            // 此语句专门执行DML语句(insert delete update)
            // 返回受影响的行数
            int count = smt.executeUpdate(sql2);
            System.out.println(count == 1 ? "操作成功" : "操作失败");

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6.关闭资源
            // 要遵循从小到大依次关闭
            if (smt != null) {
                try {
                    smt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
代码语言:javascript
复制
public static void main(String[] args) {
    try {
        // 第二种方式注册驱动,通过反射机制导致类加载
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/testdb", "root", "xcfx");
        System.out.println(conn);
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
}

通过配置文件方式获取数据库信息

代码语言:javascript
复制
package day1;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;

public class JdbcTest03 {
    public static void main(String[] args) {
        // 资源绑定器
        ResourceBundle bundle = ResourceBundle.getBundle("day1/configbase");
        String driver = bundle.getString("driver");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");

        Connection conn = null;
        Statement smtt = null;
        try {
            Class.forName(driver);

            conn = DriverManager.getConnection(url,user,password);

            smtt = conn.createStatement();

            String sql = "update employee set Salary = 5000 where Name = '张三'";

            int count = smtt.executeUpdate(sql);

            System.out.println(count == 1 ? "操作成功" : "操作失败");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            if (smtt != null) {
                try {
                    smtt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

配置文件

代码语言:javascript
复制
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/testdb
user=root
password=xcfx

处理查询结果集(遍历结果集)

代码语言:javascript
复制
package day1;

import java.sql.*;

// 处理查询结果集(遍历结果集)
public class JdbcTest04 {
    public static void main(String[] args) {
        Connection sc = null;   // 连接对象
        Statement smt = null;   // 操作对象
        ResultSet re = null;    // 结果集
        try {
            // 1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2.获取连接
            sc = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb","root","xcfx");
            // 3.获取数据库操作对象
            smt = sc.createStatement();
            // 4.执行SQL语句
            String sql = "select * from employee";
            // int executeUpdate(insert/update/delete)
            // Resultset executeQuery(select)
            re = smt.executeQuery(sql); // executeQuery专门执行dql语句的方法,返回单个resultset对象
            // 5.处理查询结果集
            while (re.next()) {
                // getString方法可以以列表下表获取,也可以以列的名字获取
                // 如果列有别名,则按照别名获取
                // 除getstring以string类型取出数据外,还可以以其他类型取出数据
                // 好处是可以直接做数学运算
                int n1 = re.getInt(1);
                String n2 = re.getString(2);
                String n3 = re.getString("gender");
                String n4 = re.getString("salary");
                String n5 = re.getString("birthday");
                String n6 = re.getString("starid");
                System.out.println((n1 + 1) + "\t" + n2 + "\t" + n3 + "\t" + n4 + "\t" + n5 + "\t" + n6);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.释放资源(从小到大)
            if (re != null) {
                try {
                    re.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (smt != null) {
                try {
                    smt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (sc != null) {
                try {
                    sc.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

模拟用户登录

代码语言:javascript
复制
package day1;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JdbcTest05 {
    public static void main(String[] args) {
        // 界面初始化
        Map<String,String> ui = intoUI();
        // 验证用户名和密码
        boolean loginSuccess = login(ui);
        // 最后输出结果
        System.out.println(loginSuccess ? "登陆成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param ui 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> ui) {
        // JDBC代码
        Connection conn = null;
        Statement smt = null;
        ResultSet re = null;
        // 表示
        boolean flag = false;
        try {
            // 1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
            // 3.获取数据库操作对象
            smt = conn.createStatement();
            // 4.执行SQL语句
            String sql = "select * from t_user where user = '"+ui.get("userName")+"' and pwd = '"+ui.get("pwd")+"'";
            re = smt.executeQuery(sql);
            // 5.处理查询结果集
            if (re.next()) {
                flag = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.释放资源
            if (re != null) {
                try {
                    re.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (smt != null) {
                try {
                    smt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        return flag;
    }

    /**
     * 初始化界面
     * @return 用户输入的用户名或密码的信息
     */
    private static Map<String, String> intoUI() {
        Scanner sc = new Scanner(System.in);
        System.out.print("用户名:");
        String userName = sc.next();
        System.out.print("密码:");
        String pwd = sc.next();

        Map<String,String> userlogin = new HashMap<String,String>();
        userlogin.put("userName",userName);
        userlogin.put("pwd",pwd);
        return userlogin;
    }
}
  • 上述代码存在SQL注入问题例:用户名:fa 密码:fa’or’1’=’1 登陆成功
截图
截图

如果上述SQL中包含关键字,那么此处已经完成了SQL语句的拼接,此时SQL会发送给DBMS,DBMS会进行SQL编译

截图
截图

解决SQL注入问题

只要用户提供的信息不参与SQL语句的编译过程,问题就解决了 即使用户提供的信息中包含有SQL语句的关键字,但是没有参与编译,也不起作用 若想用户信息不参与SQL语句编译,那么必须使用java.sql.PreparedStatement PreparedStatement接口继承了java.sql.Statement PreparedStatement是属于预编译的数据库操作对象 PreparedStatement原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传”值”

代码语言:javascript
复制
package day1;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * 解决SQL注入问题
     * 只要用户提供的信息不参与SQL语句的编译过程,问题就解决了
     * 即使用户提供的信息中包含有SQL语句的关键字,但是没有参与编译,也不起作用
     * 若想用户信息不参与SQL语句编译,那么必须使用java.sql.PreparedStatement
     * PreparedStatement接口继承了java.sql.Statement
     * PreparedStatement是属于预编译的数据库操作对象
     * PreparedStatement原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传"值"
 */
public class JdbcTest06 {
    public static void main(String[] args) {
        // 界面初始化
        Map<String,String> ui = intoUI();
        // 验证用户名和密码
        boolean loginSuccess = login(ui);
        // 最后输出结果
        System.out.println(loginSuccess ? "登陆成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param ui 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> ui) {
        // JDBC代码
        Connection conn = null;
        PreparedStatement ps = null;    // 这里使用PreparedStatement(预编译数据库操作对象)
        ResultSet re = null;
        // 表示
        boolean flag = false;
        try {
            // 1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
            // 3.获取预编译的数据库操作对象
            // 这是SQL语句的框子(框架)一个?代表一个占位符,一个?接收一个值,?不能使用单引号括起来
            String sql = "select * from t_user where user = ? and pwd = ?";
            ps = conn.prepareStatement(sql);
            // 给占位符?传值JDBC中所有下表都从1开始
            ps.setString(1, ui.get("userName"));
            ps.setString(2, ui.get("pwd"));
            // 4.执行SQL语句
            re = ps.executeQuery();
            // 5.处理查询结果集
            if (re.next()) {
                flag = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.释放资源
            if (re != null) {
                try {
                    re.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    /**
     * 初始化界面
     * @return 用户输入的用户名或密码的信息
     */
    private static Map<String, String> intoUI() {
        Scanner sc = new Scanner(System.in);
        System.out.print("用户名:");
        String userName = sc.next();
        System.out.print("密码:");
        String pwd = sc.next();

        Map<String,String> userlogin = new HashMap<String,String>();
        userlogin.put("userName",userName);
        userlogin.put("pwd",pwd);
        return userlogin;
    }
}

Statement和PreparedStatement对比

  1. Statement存在SQL注入问题
  2. Statement编译一次执行一次,PreparedStatement编译一次执行n次,效率略高
  3. PreparedStatement会在编译阶段做类型安全检查
  4. 什么时候用Statement?项目支持SQL注入或需要进行SQL语句拼接的情况下,必须使用Statement

Statement用法示例

代码语言:javascript
复制
package day1;

import java.sql.*;
import java.util.Scanner;

public class JdbcTset07 {
    public static void main(String[] args) {
        // 模拟用户升序和降序
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入desc/asc,desc表示升序,asc表示降序");
        System.out.print("请输入:");
        String keywords = sc.next();
        // JDBC编程六步
        Connection conn = null;
        Statement smt = null;
        ResultSet re = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");

            // 获取数据库操作对象
            smt = conn.createStatement();

            // 执行SQL
            String sql = "select * from t_user order by id " + keywords;
            re = smt.executeQuery(sql);

            // 遍历结果集
            while (re.next()) {
                System.out.println(re.getString("user") + "\t" + re.getString("pwd"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if (re != null) {
                try {
                    re.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (smt != null) {
                try {
                    smt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

PreparedStatement完成增删改

代码语言:javascript
复制
package day1;

import java.sql.*;

// PreparedStatement完成增删改
public class JdbcTest08 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            // 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");

            // 获取预编译数据库操作对象
            String sql = "insert into t_user values(default,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"jack");
            ps.setString(2,"hello");

            // 执行SQL
            int count = ps.executeUpdate();
            System.out.println(count);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

JDBC事务机制

1,JDBC中的事务是自动提交的,什么是自动提交?

代码语言:javascript
复制
只要执行任意一条SQL语句,则自动提交一次。这是JDBC默认的事务行为

但是在实际的业务中,通常都是n条DML语句共同联合才能完成的,

必须保证这些DML语句在同一个事务中同时成功或同时失败

2,以下程序先验证JDBC事务是否是自动提交机制

代码语言:javascript
复制
结论,Jdbc中只要执行任意一条DML语句,就提交一次
代码语言:javascript
复制
/-----JDB代码省略

// 获取预编译数据库操作对象
String sql = "update t_user set pwd=? where id=?";
ps = conn.prepareStatement(sql);
ps.setString(1,"jack");
ps.setInt(2,3); // 修改id为3的密码

// 执行SQL
int count = ps.executeUpdate();
System.out.println(count);

// 重新给占位符传值
ps = conn.prepareStatement(sql);
ps.setString(1,"meke");
ps.setInt(2,1); // 修改id为2的密码
count = ps.executeUpdate();

System.out.println(count);

模拟银行账户转账

代码语言:javascript
复制
package day1;

import java.sql.*;

/**
 * 本节点重点,
 * 将自动提交机制修改为手动提交
 * conn.setAutoCommit(false);  开启事务
 *
 * 如果程序能运行到这里说明上述代码没有异常,事务结束,手动提交
 * conn.commit();  提交事务
 *
 * if (conn != null) {
 *     try {
 *         // 回滚事务
 *         conn.rollback();
 *     } catch (SQLException ex) {
 *         ex.printStackTrace();
 *     }
 * }
 */
public class JdbcTest10 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            // 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
            // 将自动提交机制修改为手动提交
            conn.setAutoCommit(false);  // 开启事务

            // 获取预编译数据库操作对象
            String sql = "update t_act set balance=? where actno=?";
            ps = conn.prepareStatement(sql);

            // 给?传值
            ps.setDouble(1,10000);
            ps.setInt(2,111);
            int count = ps.executeUpdate(); // 执行SQL语句

            // 此时再出现异常,则不会自动提交
            // String s = null;
            // s.toString();

            // 再次修改
            ps.setDouble(1,10000);
            ps.setDouble(2,222);
            count += ps.executeUpdate();    // 再次执行SQL语句

            System.out.println(count == 2 ? "转账成功" : "转账失败");

            // 如果程序能运行到这里说明上述代码没有异常,事务结束,手动提交
            conn.commit();  // 提交事务
        } catch (Exception e) {
            if (conn != null) {
                try {
                    // 回滚事务
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

表t_act

代码语言:javascript
复制
DROP TABLE IF EXISTS `t_act`;
CREATE TABLE `t_act`  (
  `actno` int NULL DEFAULT NULL,
  `balance` double(8, 2) NULL DEFAULT NULL
)
INSERT INTO `t_act` VALUES (111, 20000.00);
INSERT INTO `t_act` VALUES (222, 0.00);

实现模糊查询

工具类

代码语言:javascript
复制
package util;

import java.sql.*;

// JDBC工具类
public class JDBCutil {

    /**
     * 工具类中的构造方法一般都是私有的
     * 因为工具类中的方法都是静态的,不需要new对象,直接采用类名调用
     */
    private JDBCutil() {}

    // 静态代码块在类加载时执行,且执行一次
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     * @return 返回连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
    }

    /**
     * 关闭资源
     * @param conn 连接对象
     * @param ps 数据库操作对象
     * @param rs 结果集对象
     */
    public static void close(Connection conn, Statement ps, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类

代码语言:javascript
复制
package day1;

import util.JDBCutil;

import java.sql.*;

/**
 * 这个程序有两个任务
 *      1.测试JDBCutil工具类是否好用
 *      2.实现模糊查询
 */
public class JdbcTest11 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 获取连接
            conn = JDBCutil.getConnection();
            // 获取预编译的数据库操作对象
            String sql = "select * from t_user where user like ?";
            ps = conn.prepareStatement(sql);    // 编译SQL语句
            ps.setString(1,"%x%");  // 给?传值
            rs = ps.executeQuery();     // 执行SQL语句,返回结果集
            // 遍历结果集
            while (rs.next()) {
                System.out.println(rs.getString("user") + "\t" + rs.getString("pwd"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            JDBCutil.close(conn,ps,rs);
        }
    }
}

行级锁

在SQL语句select后加上for update,此数据就会锁住,其他事物不可修改

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java反射机制
    • 资源绑定器
      • 反射属性
        • 获取和设置属性值
          • 可变长参数
            • 反射方法
              • 通过反射机制调用方法
                • 反编译构造方法
                  • 调用构造方法
                    • 获取父类和父接口
                    • 注解
                      • 注解中定义属性
                        • 反射注解
                        • JDBC
                          • 处理查询结果集(遍历结果集)
                            • 模拟用户登录
                              • 解决SQL注入问题
                                • Statement和PreparedStatement对比
                                  • Statement用法示例
                                    • PreparedStatement完成增删改
                                      • JDBC事务机制
                                        • 实现模糊查询
                                          • 行级锁
                                          相关产品与服务
                                          数据库
                                          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档