zoukankan      html  css  js  c++  java
  • JDBC学习1:详解JDBC使用

    什么是JDBC

    JDBC(Java Database Connectivity),即Java数据库连接,是一种用于执行SQL语句的Java API,可以为多种关系数据库提供同一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,根据这种基准可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。总而言之,JDBC做了三件事:

    1、与数据库建立连接

    2、发送操作数据库的语句

    3、处理结果

    JDBC简单示例

    下面的代码演示了如何利用JDBC从数据库中查询若干条符合要求的数据出来,使用的数据库是MySql。

    1、建立一个数据库和一张表,我的习惯是在CLASSPATH底下建立一个.sql的文件用于存放sql语句

    create database school;
    
    use school;
    
    create table student
    (
        studentId            int                 primary key    auto_increment    not null,
        studentName        varchar(10)                                                            not null,
        studentAge        int,
        studentPhone    varchar(15)
    )
    
    insert into student values(null,'Betty', '20', '00000000');
    insert into student values(null,'Jerry', '18', '11111111');
    insert into student values(null,'Betty', '21', '22222222');
    insert into student values(null,'Steve', '27', '33333333');
    insert into student values(null,'James', '22', '44444444');
    commit;

    2、建立一个.properties文件用于存储MySql连接的几个属性。为什么要建立.properties而不在代码里面写死,由于这个并不是Java设计模式的分类,就不细讲了,只需要记住:从设计的角度看,把内容写在配置文件中永远好过把内容写死在代码中

    mysqlpackage=com.mysql.jdbc.Driver
    mysqlurl=jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf-8
    mysqlname=root
    mysqlpassword=root

    3、根据表字段建立实体类

    public class Student
    {
        private int        studentId;
        private String    studentName;
        private int        studentAge;
        private String    studentPhone;
        
        public Student(int studentId, String studentName, int studentAge,
                String studentPhone)
        {
            this.studentId = studentId;
            this.studentName = studentName;
            this.studentAge = studentAge;
            this.studentPhone = studentPhone;
        }
        
        public int getStudentId()
        {
            return studentId;
        }
    
        public String getStudentName()
        {
            return studentName;
        }
    
        public int getStudentAge()
        {
            return studentAge;
        }
    
        public String getStudentPhone()
        {
            return studentPhone;
        }
    
        public String toString()
        {
            return "studentId = " + studentId + ", studentName = " + studentName + ", studentAge = " +
                    studentAge + ", studentPhone = " + studentPhone;
        }
    }

    4、写一个DBConnection类专门用于向外提供数据库连接。我这里用了MySql,所以只有一个mysqlConnection,如果还用到了Oracle,当然还可以向外提供一个oracleConnection。把这些连接设为全局的可能有人会想是否会有线程安全问题,这是一个很好的问题。那因为我们只从Connection里面读取一个PreparedStatement出来,而不会去写它,只读不修改,是不会引发线程安全问题的。另外把Connection设置为static的保证了Connection在内存中只有一份,不会占多大资源,每次使用完不调用close()方法去关闭它也没事。至于把.properties文件读到内存中,可以参看http://www.cnblogs.com/xrq730/p/4847337.html我之前写的文章的最后

    public class DBConnection
    {    
        private static Properties properties = new Properties();
        
        static
        {
            /** 要从CLASSPATH下取.properties文件,因此要加"/" */
            InputStream is = DBConnection.class.getResourceAsStream("/db.properties");
            try
            {
                properties.load(is);
            } 
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        
        /** 这个mysqlConnection只是为了用来从里面读一个PreparedStatement,不会往里面写数据,因此没有线程安全问题,可以作为一个全局变量 */
        public static Connection mysqlConnection = getConnection();
        
        public static Connection getConnection()
        {
            Connection con = null;
            try
            {
                Class.forName((String)properties.getProperty("mysqlpackage"));
                con = DriverManager.getConnection((String)properties.getProperty("mysqlurl"), 
                        (String)properties.getProperty("mysqlname"), 
                        (String)properties.getProperty("mysqlpassword"));
            } 
            catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            } 
            catch (SQLException e)
            {
                e.printStackTrace();
            }
            return con;
        }
    }    

    5、建立一个工具类,用来写各种方法,专门和数据库进行交互。这种工具类最好搞成单例的,这样就不用每次去new出来了(实际上new出来也没看出来会有什么好处),节省资源

    package com.xrq.test11;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    
    public class StudentManager
    {
        private static StudentManager instance = new StudentManager();
        
        private StudentManager()
        {
            
        }
        
        public static StudentManager getInstance()
        {
            return instance;
        }
        
        public List<Student> querySomeStudents(String studentName) throws Exception
        {
            List<Student> studentList = new ArrayList<Student>();
            Connection connection = DBConnection.mysqlConnection;
            PreparedStatement ps = connection.prepareStatement("select * from student where studentName = ?");
            ps.setString(1, studentName);
            ResultSet rs = ps.executeQuery();
            
            Student student = null;
            while (rs.next())
            {
                student = new Student(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getString(4));
                studentList.add(student);
            }
            
            ps.close();
            rs.close();
            return studentList;
        }
    }

    6、写个main函数去调用一下

    List<Student> studentList = StudentManager.getInstance().querySomeStudents("Betty");
    for (Student student : studentList) {
        System.out.println(student);
    }

    7、看一下运行结果,和数据库里面的一样,成功

    studentId = 1, studentName = Betty, studentAge = 20, studentPhone = 00000000
    studentId = 3, studentName = Betty, studentAge = 21, studentPhone = 22222222

    为什么要使用占位符"?"

    看一下第5点,大家一定注意到了,写sql语句的时候用了"?"占位符,当然有美化代码的因素,不用占位符就要在括号里写"+"来拼接参数,如果要拼接的参数一多,代码肯定不好看,可读性不强。但是除了这个原因,还有另外一个重要的原因,就是避免一个安全问题。假设我们不用占位符写sql语句,那"querySomeStudents(String name) throws Exception"方法就要这么写:

    public List<Student> querySomeStudents(String studentName) throws Exception
    {
        List<Student> studentList = new ArrayList<Student>();
        Connection connection = DBConnection.mysqlConnection;
        PreparedStatement ps = connection.prepareStatement("select * from student where studentName = '" + studentName + "'");
        ResultSet rs = ps.executeQuery();
            
        Student student = null;
        while (rs.next())
        {
            student = new Student(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getString(4));
            studentList.add(student);
        }
            
        ps.close();
        rs.close();
        return studentList;
    }

    上面的main函数一样可以获取到两条数据,但是问题来了,如果我这么调用呢:

    public static void main(String[] args) throws Exception
        {
            List<Student> studentList = new ArrayList<Student>();
            studentList = StudentManager.getInstance().querySomeStudents("' or '1' = '1");
            for (Student student : studentList)
                System.out.println(student);
        }

    看下运行结果:

    studentId = 1, studentName = Betty, studentAge = 20, studentPhone = 00000000
    studentId = 2, studentName = Jerry, studentAge = 18, studentPhone = 11111111
    studentId = 3, studentName = Betty, studentAge = 21, studentPhone = 22222222
    studentId = 4, studentName = Steve, studentAge = 27, studentPhone = 33333333
    studentId = 5, studentName = James, studentAge = 22, studentPhone = 44444444

    为什么?看下拼接之后的sql语句就知道了:

    select * from student where studentName = '' or '1' = '1'

    '1'='1'永远成立,所以前面的查询条件是什么都没用。这种问题是有应用场景的,不是随便写一下。Java越来越多的用在Web上,既然是Web,那么查询的时候有一种情况就是用户输入一个条件,后台获取到查询条件,拼接sql语句查数据库,有经验的用户完全可以输入一个"‘'' or '1' = '1",这样就拿到了库里面的所有数据了。

    JDBC事物

    谈数据库必然离不开事物,事物简单说就是"要么一起成功,要么一起失败"。那简单往前面的StudentManager里面写一个插入学生信息的方法:

    public void addStudent(String studentName, int studentAge, String studentPhone) throws Exception
    {
        Connection connection = DBConnection.mysqlConnection;
        PreparedStatement ps = connection.prepareStatement("insert into student values(null,?,?,?)");
        ps.setString(1, studentName);
        ps.setInt(2, studentAge);
        ps.setString(3, studentPhone);
        if (ps.executeUpdate() > 0)
            System.out.println("添加学生信息成功");
        else
            System.out.println("添加学生信息失败");    
    }
    public static void main(String[] args) throws Exception
    {
        StudentManager.getInstance().addStudent("Betty", 17, "55555555");
    }

    运行就不运行了,反正最后结果是"添加学生信息成功",数据库里面多了一条数据。注意一下:

    1、增删改用的是executeUpdate()方法,因为增删改认为都是对数据库的更新

    2、查询用的是executeQuery()方法,看名字就知道了"Query",查询嘛

    可能有人注意到一个问题,就是Java代码在insert后并没有对事物进行commit,数据就添加进数据库了,也能查出来,这是为什么呢?因为JDK的Connection设置了事物的自动提交。如果在addStudent(...)方法里面这么写:

    Connection connection = DBConnection.mysqlConnection;
    connection.setAutoCommit(false);

    autoCommit这个属性原来是true,JDK自然会帮助开发者自动提交事物了。OK,如果要改成手动提交事物的代码,那么应该这么写addStudent(...)方法:

    public void addStudent(String studentName, int studentAge, String studentPhone) throws Exception
    {
        Connection connection = DBConnection.mysqlConnection;
        connection.setAutoCommit(false);
        PreparedStatement ps = connection.prepareStatement("insert into student values(null,?,?,?)");
        ps.setString(1, studentName);
        ps.setInt(2, studentAge);
        ps.setString(3, studentPhone);
        try
        {
            ps.executeUpdate();
            connection.commit();
        } 
        catch (Exception e)
        {
            e.printStackTrace();
            connection.rollback();
        }
    }

    要记得抛异常的时候利用rollback()方法回滚掉事物。

  • 相关阅读:
    strpbrk — 在字符串中查找一组字符的任何一个字符
    strip_tags — 从字符串中去除 HTML 和 PHP 标记
    str_word_count — 返回字符串中单词的使用情况
    str_split — 将字符串转换为数组
    每日总结
    每日总结
    每日总结
    每日总结
    每日总结
    每日总结
  • 原文地址:https://www.cnblogs.com/xrq730/p/4851648.html
Copyright © 2011-2022 走看看