zoukankan      html  css  js  c++  java
  • JDBC 使用说明(含知识介绍,Mysql使用实例)

    JDBC介绍

    JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。
    在这里插入图片描述

    JDBC 架构

    分为双层架构和三层架构。

    双层

    在这里插入图片描述
    作用:此架构中,Java Applet 或应用直接访问数据源。

    条件:要求 Driver 能与访问的数据库交互。

    机制:用户命令传给数据库或其他数据源,随之结果被返回。

    部署:数据源可以在另一台机器上,用户通过网络连接,称为 C/S配置(可以是内联网或互联网)。

    三层

    在这里插入图片描述
    侧架构特殊之处在于,引入中间层服务。

    流程:命令和结构都会经过该层。

    吸引:可以增加企业数据的访问控制,以及多种类型的更新;另外,也可简化应用的部署,并在多数情况下有性能优势。

    历史趋势: 以往,因性能问题,中间层都用 C 或 C++ 编写,随着优化编译器(将 Java 字节码 转为 高效的 特定机器码)和技术的发展,如EJB,Java 开始用于中间层的开发这也让 Java 的优势突显出现出来,使用 Java 作为服务器代码语言,JDBC随之被重视。

    JDBC编程步骤

    1.装载相应数据库的JDBC驱动并进行初始化

    1.1导入专用的jar包(不同的数据库用到的jar包不同)

    • 访问MySQL数据库需要用到第三方的类,这些第三方的类,都被压缩在一个.Jar的文件里。mysql-connector-java-5.0.8-bin.jar包可以在网上下载,或者在MySQL的安装目录下找到。通常下载到该jar包之后将其放到在项目的lib目录下,在本例就会放在E:projectj2selib 这个位置,然后在eclipse中导入这个jar包。

    导包步骤:
    中文:右键项目 ->构建路径->配置构建路径->添加外部Jar

    在这里插入图片描述

    英文:右键project->property->java build path->libaries->add external jars
    在这里插入图片描述

    如果没有完成上述步骤的导包操作,后面会抛出ClassNotFoundException

    1.2初始化驱动

    通过初始化驱动类com.mysql.jdbc.Driver,该类就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle数据库那么该驱动类将不同。

    注意:Class.forName需要捕获ClassNotFoundException.

    try {
            Class.forName("com.mysql.jdbc.Driver");		
            } catch (ClassNotFoundException e) { 				
                e.printStackTrace();
            }
    

    Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。

    2.建立JDBC和数据库之间的Connection连接

    这里需要提供:数据库服务端的IP地址:127.0.0.1 (这是本机,如果连接其他电脑上的数据库,需填写相应的IP地址。也可以填写localhost)

      • 数据库的端口号: 3306 (mysql端口号)
        数据库名称 exam (根据你自己数据库中的名称填写)
        编码方式 UTF-8
        账号 root
        密码 admin(如果你在创建数据库的时候没有使用默认的账号和密码,请填写自己设置的账号和密码。有些数据库的默认密码为空)
    Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");
    

    Connection是与特定数据库连接回话的接口,使用的时候需要导包,而且必须在程序结束的时候将其关闭。getConnection方法也需要捕获SQLException异常。

    因为在进行数据库的增删改查的时候都需要与数据库建立连接,所以可以在项目中将建立连接写成一个工具方法,用的时候直接调用即可:

            /**
    	 * 取得数据库的连接
    	 * @return 一个数据库的连接
    	 */
    public static Connection getConnection(){
    		Connection conn = null;
    		 try {
    			 	//初始化驱动类com.mysql.jdbc.Driver
    	            Class.forName("com.mysql.jdbc.Driver");
    	            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8","root", "admin");
    	            //该类就在 mysql-connector-java-5.0.8-bin.jar中,如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException
    	        } catch (ClassNotFoundException e) { 				
    	            e.printStackTrace();
    	        }catch (SQLException e) {							
    	            e.printStackTrace();
    	        }
    		 return conn;
    	}
    

    3.创建Statement或者PreparedStatement接口,执行SQL语句

    3.1 使用Statement接口

    Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。其中 ,增删改只需要改变SQL语句的内容就能完成,然而查询略显复杂。在Statement中使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点,具体在下文中的对比中介绍。所以Statement在实际过程中使用的非常的少,所以具体的放到PreparedStatement那里给出详细代码。

    字符串拼接方式的SQL语句是非常繁琐的,中间有很多的单引号和双引号的混用,极易出错。

    Statement s = conn.createStatement();
    // 准备sql语句
    // 注意: 字符串要用单引号'
    String sql = "insert into t_courses values(null,"+"'数学')";
    //在statement中使用字符串拼接的方式,这种方式存在诸多问题
    s.execute(sql);
    System.out.println("执行插入语句成功");
    

    3.2使用PreparedStatement接口

    与 Statement一样,PreparedStatement也是用来执行sql语句的与创建Statement不同的是,需要根据sql语句创建PreparedStatement。除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。

    给数据库中添加课程: (以下代码中最后关闭资源的两个方法 DbUtil.close(pstmt); DbUtil.close(conn); 和上面的建立连接的方法是一样的,是在工具类中定义了的关闭方法,下文会给出其代码

            /**
    	 * 添加课程
    	 * @param courseName 课程名称
    	 */
    	public void addCourse(String courseName){
    		String sql = "insert into t_course(course_name) values(?)";  
     //该语句为每个 IN 参数保留一个问号(“?”)作为占位符
    		Connection conn = null;				//和数据库取得连接
    		PreparedStatement pstmt = null;		//创建statement
    		try{
    			conn = DbUtil.getConnection();
    			pstmt = (PreparedStatement) conn.prepareStatement(sql);
    			pstmt.setString(1, courseName); //给占位符赋值
    			pstmt.executeUpdate();			//执行
    		}catch(SQLException e){
    			e.printStackTrace();
    		}
    		finally{
    			DbUtil.close(pstmt);
    			DbUtil.close(conn);		//必须关闭
    		}
    	}
    

    对数据库中的课程进行删除:

            /**
    	 * 删除课程
    	 * @param courseId
    	 */
    	public void delCourse(int courseId){
    		String sql = "delete from t_course where course_id = ?";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		try {
    			conn = DbUtil.getConnection();
    			pstmt = (PreparedStatement) conn.prepareStatement(sql);
    			pstmt.setInt(1, courseId);
    			pstmt.executeUpdate();
    		} catch (SQLException e) {
    			// TODO: handle exception
    			e.printStackTrace();
    		}finally{
    			DbUtil.close(pstmt);
    			DbUtil.close(conn);		//必须关闭
    		}
    	}
    

    对数据库中的课程进行修改:

            /**
    	 * 修改课程
    	 * @param courseId
    	 * @param courseName
    	 */
    	public void modifyCourse(int courseId,String courseName){
    		String sql = "update t_course set course_name =? where course_id=?";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		try {
    			conn = DbUtil.getConnection();
    			pstmt = (PreparedStatement) conn.prepareStatement(sql);
    			pstmt.setString(1, courseName);  //利用Preparedstatement的set方法给占位符赋值
    			pstmt.setInt(2, courseId);
    			pstmt.executeUpdate();
    		} catch (SQLException e) {
    			// TODO: handle exception
    			e.printStackTrace();
    		}finally{
    			DbUtil.close(pstmt);
    			DbUtil.close(conn);		//必须关闭
    		}
    	}
    

    3.3本节小结

    由上面的增删改程序可以看出,他们的代码都是大同小异的,主要是SQL语句存在差异,其他的地方几乎是完全相同的。其中有几个地方需要注意:

    1.使用PreparedStatement时,他的SQL语句不再采用字符串拼接的方式,而是采用占位符的方式。“?”在这里就起到占位符的作用。这种方式除了避免了statement拼接字符串的繁琐之外,还能够提高性能。每次SQL语句都是一样的,java类就不会再次编译,这样能够显著提高性能。

    String sql = "update t_course set course_name =? where course_id=?";
    

    后面需要用到PreparedStatement接口创建的pstmt的set方法给占位符进行赋值。注意一点,这里的参数索引是从1开始的。

    pstmt = (PreparedStatement) conn.prepareStatement(sql);
    pstmt.setString(1, courseName);  //利用Preparedstatement的set方法给占位符赋值
    pstmt.setInt(2, courseId);
    pstmt.executeUpdate();
    

    增删改都使用pstmt.executeUpdate();语句进行SQL语句的提交 ,下文的查询会有所不同,请注意。

    2.在添加的过程的,如果添加的数据量比较大的话,可以用批量添加。 PreparedStatement接口提供了相应的批量操作的方法。

    for(int i=1;i<100;i++){
         pstmt.setInt(1,8000+i);
         pstmt.setString(2,"赵_"+i);
         pstmt.addBatch();
    //批量更新
         if(i%10==0){
         pstmt.executeBatch();
        }
    }
    

    下面我们来看稍显麻烦一点的查询操作:

            /**
    	 * 查询课程
    	 * @return
    	 */
    	public List<Course> findCourseList(){
    		String sql = "select * from t_course order by course_id";
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		//创建一个集合对象用来存放查询到的数据
    		List<Course> courseList = new ArrayList<>();
    		try {
    			conn = DbUtil.getConnection();
    			pstmt = (PreparedStatement) conn.prepareStatement(sql);
    			rs = (ResultSet) pstmt.executeQuery();
    			while (rs.next()){
    				int courseId = rs.getInt("course_id");
    				String courseName = rs.getString("course_name");
    				//每个记录对应一个对象
    				Course course = new Course();
    				course.setCourseId(courseId);
    				course.setCourseName(courseName);
    				//将对象放到集合中
    				courseList.add(course);
    			}
    		} catch (SQLException e) {
    			// TODO: handle exception
    			e.printStackTrace();
    		}finally{
    			DbUtil.close(pstmt);
    			DbUtil.close(conn);		//必须关闭
    		}
    		return courseList;
    	}
    

    查询操作使用executeQuery()进行更新。其他相关的问题放在第四步(处理和显示结果)中解释。

    4.处理和显示结果

    执行查询语句,并把结果集返回给集合ResultSet

    ResultSet rs = s.executeQuery(sql);
    

    利用While(ResultSet.next()){…}循环将集合ResultSet中的结果遍历出来。

    ResultSet.getXX(); 这里的get方法的括号里面可以填属性值,如下图代码中的course_id,还可以填该属性在数据表中的列号,从1开始编码,例如:course_id在我的t-courses数据表中位于第一列,所以执行get方法的时候,我除了代码段中写法外,还可以这样写int courseId = rs.getInt(1);但是不推荐使用列号的这种方式,因为一段数据表中个属性值的顺序发生变化,就会导致这里出错,而使用属性名则不会出现这样的问题。

    while (rs.next()){
    		int courseId = rs.getInt("course_id");
    		String courseName = rs.getString("course_name");
    		//每个记录对应一个对象
    		Course course = new Course();
    //在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,也可以直接打印到控制台
    		course.setCourseId(courseId);
    		course.setCourseName(courseName);
    		//将对象放到集合中
    		courseList.add(course);
    		}
    

    还有一点需要说明的是:
    因为在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,你也可以直接用打印语句将CourseId和CourseName打印到控制台。

     course.setCourseId(courseId);
     course.setCourseName(courseName); 
    

    5.释放资源

    在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。关闭的过程中遵循从里到外的原则。因为在增删改查的操作中都要用到这样的关闭操作,为了使代码简单,增加其复用性,这里我将这些关闭的操作写成一个方法和建立连接的方法一起放到一份工具类中。

    /**
    	 * 封装三个关闭方法
    	 * @param pstmt
    	 */
    	public static void close(PreparedStatement pstmt){
    		if(pstmt != null){						//避免出现空指针异常
    			try{
    				pstmt.close();
    			}catch(SQLException e){
    				e.printStackTrace();
    			}
    			
    		}
    	}
    	
    	public static void close(Connection conn){
    		if(conn != null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				// TODO: handle exception
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	public static void close(ResultSet rs){
    		if (rs != null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				// TODO: handle exception
    				e.printStackTrace();
    			}
    		}
    	}
    

    小结

    JDBC编程的内容就这些了,如果你已经全部掌握,还有事务、获取自增、获取元数据、ORM、DAO、数据连接池等内容可以自行了解一下。
    另外,Statement和PreparedStatement的异同,execute和executeUpdate的区别等内容,这里做一些介绍。

    Statement和PreparedStatement的异同及优缺点

    同:两者都是用来执SQL语句的

    异:PreparedStatement需要根据SQL语句来创建,它能够通过设置参数,指定相应的值,不是像Statement那样使用字符串拼接的方式。

    PreparedStatement的优点:

    1、其使用参数设置,可读性好,不易记错。在statement中使用字符串拼接,可读性和维护性比较差。

    2、其具有预编译机制,性能比statement更快。

    3、其能够有效防止SQL注入攻击。

    execute和executeUpdate的区别

    相同点:二者都能够执行增加、删除、修改等操作。

    不同点:

    1、execute可以执行查询语句,然后通过getResult把结果取出来。executeUpdate不能执行查询语句。

    2、execute返回Boolean类型,true表示执行的是查询语句,false表示执行的insert、delete、update等。executeUpdate的返回值是int,表示有多少条数据受到了影响。

  • 相关阅读:
    51nod 1380 夹克老爷的逢三抽一 堆 脑洞题
    洛谷P2168 荷马史诗 堆 哈夫曼树
    HDU 4343 Interval query 倍增思想, DP
    洛谷P1969 积木大赛 贪心 差分
    codves1052 地鼠游戏 贪心
    hdu6031 Innumerable Ancestors
    Codeforces 278C Learning Languages(并查集) 求连通块
    [LeetCode]80. Remove Duplicates from Sorted Array II删除数组中的重复值
    [LeetCode]86. Partition List分离链表
    [LeetCode]42. Trapping Rain Water雨水填坑
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13308059.html
Copyright © 2011-2022 走看看