(JDBC)01_JDBC基本使用

一、JDBC概述

1.1数据的持久化

持久化(persistence):**把数据保存到可掉电式存储设备中以供之后使用。**大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
在这里插入图片描述

2.Java中的数据存储技术

  1. 在Java中,数据库存取技术可分为如下几类:
    JDBC直接访问数据库
    JDO (Java Data Object )技术
    第三方O/R工具,如Hibernate, Mybatis 等
  2. JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。

3.JDBC介绍

  1. JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql
    使用这些类库可以以一种标准的方法、方便地访问数据库资源。
  2. JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
  3. JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
  4. 如果没有JDBC,那么Java程序访问数据库时是这样的:
    在这里插入图片描述
  5. 有了JDBC,Java程序访问数据库时是这样的:
    在这里插入图片描述
    在这里插入图片描述

4.JDBC体系结构

  1. JDBC接口(API)包括两个层次:
    ①面向应用的API: Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
    ②面向数据库的API: Java Driver API,供开发商开发数据库驱动程序用。

JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。

5.JDBC程序编写步骤

在这里插入图片描述

补充:ODBC(Open Database Connectivity,开放式数据库连接),是微软在Windows平台下推出的。使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。

二、获取数据库连接

1.要素一:Driver接口实现类

1.1.Driver接口介绍

  1. java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
  2. 在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
    Oracle的驱动:oracle.jdbc.driver.OracleDriver
    mySql的驱动:com.mysql.jdbc.Driver
    1 获取数据库链接驱动
    oracle的驱动包:
    在这里插入图片描述
    mysql的驱动包:
    在这里插入图片描述
    2 创建一个java project
    3 在工程目录下 创建一个目录 libs
    4 将数据库的驱动包拷贝到libs目录下
    5 将数据库驱动包添加到类路径下

    在这里插入图片描述
    在这里插入图片描述

2.要素二:URL

  1. JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。
  2. JDBC URL的标准由三部分组成,各部分间用冒号分隔。
    jdbc:子协议:子名称
    协议: JDBC URL中的协议总是jdbc
    子协议: 子协议用于标识一个数据库驱动程序
    子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
    举例:
    在这里插入图片描述

几种常用数据库的 JDBC URL**

  1. MySQL的连接URL编写方式:
    jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
mysql: jdbc:mysql://localhost:3306/zb?useUnicode=true&characterEncoding=utf8
  1. Oracle 11g的连接URL编写方式:
    jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称
jdbc:oracle:thin:@192.168.112.128:1521:orcl

3.要素三:用户名和密码

  1. user,password可以用“属性名=属性值”方式告诉数据库
  2. 可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接

4.数据库连接方式举例

1.连接方式一:

@Test
	public void connectionTest() throws SQLException {
		
		////1.提供java.sql.Driver接口实现类的对象
		Driver driver = new com.mysql.jdbc. Driver();
		//2.提供url,指明具体操作的数据
		String url = "jdbc:mysql://localhost:3306/lanqiao";
		//3.提供Properties的对象,指明用户名和密码
		Properties pro = new Properties();
		pro.setProperty("user", "root");
		pro.setProperty("password",	"root");
		//4.调用driver的connect(),获取连接
		Connection conn = driver.connect(url, pro);
		System.out.println(conn);
	}

访问时可能存在的问题:
在这里插入图片描述
解决方法(赋予权限):

grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
flush privileges;

方式一存在是的应用程序和Mysql的jdbc的实现类发生了直接的耦合

2.连接方式二

@Test
public void connectionTest2() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
	
	 //1.实例化Driver
	String driverStr = "com.mysql.jdbc.Driver";//Driver类的全类名
	Class clazz = Class.forName(driverStr);
	Driver driver =  (Driver) clazz.newInstance();
	
	//2.提供url,指明具体操作的数据
	String url = "jdbc:mysql://localhost:3306/lanqiao";
	//3.提供Properties的对象,指明用户名和密码
	Properties pro = new Properties();
	pro.setProperty("user", "root");
	pro.setProperty("password",	"root");
	//4.调用driver的connect(),获取连接
	Connection conn = driver.connect(url, pro);
	System.out.println(conn);
}

使用反射机制 来解除耦合
说明:相较于方式一,这里使用反射实例化Driver,不在代码中体现第三方数据库的API。体现了面向接口编程思想。

3.连接方式三:DriverManager使用注册驱动

@Test
	public void connectionTest3() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		//1.数据库连接的4个基本要素:
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "root";
            String driverName = "com.mysql.jdbc.Driver";
		//2.实例化Driver
            Class clazz = Class.forName(driverName);
            Driver driver = (Driver) clazz.newInstance();
		//3.注册驱动
            DriverManager.registerDriver(driver);
		//4.获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            System.out.println(conn);
	}

说明:使用DriverManager实现数据库的连接。体会获取连接必要的4个基本要素。
在这里插入图片描述

4.连接方式四

@Test
public void connectionTest4() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
	//定义URL
	String url = "jdbc:mysql://localhost:3306/lanqiao";
	//创建driver
	String driverStr = "com.mysql.jdbc.Driver";//Driver类的全类名
	//加载mysql的Dirver
	Class.forName(driverStr);
	//通过驱动来获取链接
	Properties pro = new Properties();
	pro.setProperty("user", "root");
	pro.setProperty("password",	"root");
	Connection conn = DriverManager.getConnection(url, pro);
	System.out.println(conn);
}
@Test
	public void connectionTest4() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		            //1.数据库连接的4个基本要素:
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "abc123";
            String driverName = "com.mysql.jdbc.Driver";
            //2.加载驱动 (①实例化Driver ②注册驱动)
            Class.forName(driverName);
            //Driver driver = (Driver) clazz.newInstance();
            //3.注册驱动
            //DriverManager.registerDriver(driver);
            /*
            可以注释掉上述代码的原因,是因为在mysql的Driver类中声明有:
            static {
                try {
                    DriverManager.registerDriver(new Driver());
                } catch (SQLException var1) {
                    throw new RuntimeException("Can't register driver!");
                }
            }
             */
            //3.获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            System.out.println(conn);
	}

存在的问题 :连接属性采用了硬编码的方式

5.连接方式五(最终版)

说明:使用配置文件的方式保存配置信息,在代码中加载配置文件
使用配置文件的好处:
①实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码
②如果修改了配置信息,省去重新编译的过程。

解决硬编码的问题
建立配置属性文件
在这里插入图片描述
jdbc.properties

url=jdbc:mysql://localhost:3306/lanqiao
driver =com.mysql.jdbc.Driver
user=root
password=root

jdbc链接数据库的最终版

@Test
	public void connectionTest5() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
		//第一步加载配置文件
		InputStream in =this.getClass().getClassLoader().getSystemResourceAsStream("jdbc.properties");
		
		Properties pro = new Properties();
		pro.load(in);
		//定义URL
		String url = pro.getProperty("url");
		//从配置文件中获取链接属性
		String driverStr = pro.getProperty("driver");//Driver类的全类名
		String user = pro.getProperty("user");
		String password=pro.getProperty("password");
		//加载mysql的Dirver
		Class.forName(driverStr);
		Connection conn = DriverManager.getConnection(url, user,password);
		System.out.println(conn);
	}

注意:
必须将配置文件放在src目录下,否则会出现空指针异常和配置文件异常
在这里插入图片描述
链接oracle数据库

#oracle
oracle.url=jdbc:oracle:thin:@192.168.112.128:1521:orcl
oracle.driver =oracle.jdbc.driver.OracleDriver
oracle.user=hr
oracle.password=hr
@Test
	public void connectionTest6() throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
		//第一步加载配置文件
		InputStream in =this.getClass().getClassLoader().getSystemResourceAsStream("jdbc.properties");
		Properties pro = new Properties();
		pro.load(in);
		//定义URL
		String url = pro.getProperty("oracle.url");
		//创建driver
		String driverStr = pro.getProperty("oracle.driver");//Driver类的全类名
		//创建用户名和密码
		String user = pro.getProperty("oracle.user");
		String password=pro.getProperty("oracle.password");
		//加载mysql的Dirver
		Class.forName(driverStr);
		Connection conn = DriverManager.getConnection(url, user,password);
		System.out.println(conn);
	}

说明:使用配置文件的方式保存配置信息,在代码中加载配置文件
使用配置文件的好处:
①实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码
②如果修改了配置信息,省去重新编译的过程。

三、使用PreparedStatement实现CRUD操作

  1. 数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。
  2. 在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
    Statement: 用于执行静态 SQL 语句并返回它所生成结果的对象。
    PrepatedStatement: SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
    CallableStatement: 用于执行 SQL 存储过程

1.使用Statement操作数据表的弊端

在这里插入图片描述
获取连接:

public static Connection getConnection() throws ClassNotFoundException, IOException, SQLException {
		InputStream in = Class.forName("org.lanqiao.jdbc.StatementTest").getClass().getClassLoader().getSystemResourceAsStream("jdbc.properties");
		Properties pro = new Properties();
		pro.load(in);
		String url = pro.getProperty("url");
		String user = pro.getProperty("user");
		String password = pro.getProperty("password");
		String driver = pro.getProperty("driver");
		
		Class.forName(driver);
		
		Connection conn = DriverManager.getConnection(url, user, password);
		return conn;
		
	}

插入数据:

//插入数据
@Test
	public void teststmtInsert() throws ClassNotFoundException, IOException, SQLException {
		Connection conn = StatementTest.getConnection();
		Statement stmt  = conn.createStatement();
		String sql = "INSERT into student(stu_id,stu_age,stu_sex,stu_name)" + 
				"VALUES(5,20,'女','wangba')";
		boolean res = stmt.execute(sql);
		System.out.println(res );
	}

如果出现中文乱码 请使用一下URL

mysql.url=jdbc:mysql:///lanqiao?useUnicode=true&characterEncoding=utf8

更新数据:

//执行更新
	@Test
	public void teststmtUpdate() throws ClassNotFoundException, IOException, SQLException {
		Connection conn = StatementTest.getConnection();
		Statement stmt  = conn.createStatement();
		String sql = "update student set stu_name = '花花',cls_id = 1 where stu_id = 5";
		boolean res = stmt.execute(sql);
		System.out.println(res );
	}

删除数据:

//删除数据
	@Test
	public void teststmtDelete() throws ClassNotFoundException, IOException, SQLException {
		Connection conn = StatementTest.getConnection();
		Statement stmt  = conn.createStatement();
		String sql = "delete from student where stu_id = 2";
		boolean res = stmt.execute(sql);
		System.out.println(res );
	}

在这里插入图片描述

@Test
	public void testStmtDelete() throws Exception {
		// 获取数据库链接
		Connection conn = StatementTest.getConnection();
		Statement stmt = conn.createStatement();
		String sql = "delete from emp where empno=1017";
		int res = stmt.executeUpdate(sql);//返回值表示受影响行数
		System.out.println(res);
	}

获取自动生成的主键 一般用在mysql、oracle中的新增操作 :
在这里插入图片描述

// 执行更新
	@Test
	public void testStmtInsert() throws Exception {
		// 获取数据库链接
		Connection conn = StatementTest.getConnection();
		Statement stmt = conn.createStatement();
		String sql = "INSERT into emp(ename,job,mgr,hiredate,sal,COMM,deptno)"
				+ " values('李四','经理',1001,now(),5000,500,10)";
		int res = stmt.executeUpdate (sql,Statement.RETURN_GENERATED_KEYS);
		System.out.println(res);
		//获取生成的主键
		ResultSet  rs=stmt.getGeneratedKeys();
		while(rs.next()) {
			int key = rs.getInt(1);//列的索引从1开始
			System.out.println(key);
		}
	}

执行查询
返回单行记录

// 执行查询 查询单个对象
	@Test
	public void testStmtQuery() throws Exception {
		// 获取数据库链接
		Connection conn = StatementTest.getConnection();
		Statement stmt = conn.createStatement();
		String sql = "select * from emp where empno=1001";
		ResultSet rs = stmt.executeQuery(sql);
		while(rs.next()) {
			String ename = rs.getString("ename");
			String job = rs.getString(3);
			Date hiredate = rs.getDate("hiredate");
			double sal = rs.getDouble("sal");
			System.out.println(ename+"--"+job+"--"+hiredate+"-"+sal);
		}
	}
// 执行查询 查询多行记录
		@Test
		public void testStmtQuery2() throws Exception {
			// 获取数据库链接
			Connection conn = StatementTest.getConnection();
			Statement stmt = conn.createStatement();
			String sql = "select * from emp ";
			ResultSet rs = stmt.executeQuery(sql);
			while(rs.next()) {
				String ename = rs.getString("ename");
				String job = rs.getString(3);
				Date hiredate = rs.getDate("hiredate");
				double sal = rs.getDouble("sal");
				System.out.println(ename+"--"+job+"--"+hiredate+"-"+sal);
			}
		}

SQL注入(弊端):

//使用statement的问题
		//模拟用户登录  通过用户输入用户名和密码  查询该用户是否存在 如果存在则说明用户名和密码正确
		//此时可以正常登录
		@Test
		public  void login() throws Exception {
			Scanner sc = new Scanner(System.in);
			System.out.println("请输入您的用户名:");
			String username = sc.nextLine();
			System.out.println("请输入密码");
			String password = sc.nextLine();
			Connection conn = getConnection();
			String sql = "select * from users where username='"+username+"' and password='"+password+"'";
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			users ur = null;
			while(rs.next()) {
				//将查询结果封装为一个对象
				ur = new Users();
				ur.setUid(rs.getInt("uid"));
				ur.setUsername(rs.getString("username"));
				ur.setAge(rs.getInt("age"));
				ur.setBirthday(rs.getDate("birthday"));
				ur.setRealName(rs.getString("realname"));
			}
			if(ur != null) {
				System.out.println("登录成功");
			}else {
				System.out.println("登录失败");
			}
			sc.close();
		}

user实例类
在这里插入图片描述
应用表的结构与数据
在这里插入图片描述
在这里插入图片描述
第一种:

在这里插入图片描述
第二种:
在这里插入图片描述
在这里插入图片描述
使用statement 会存在sql注入的风险

ResultSet与ResultSetMetaData

获取ResultSet中的列的数量类型及属性
在这里插入图片描述

在这里插入图片描述

ResultSetMetaData  rsmd = rs.getMetaData();//获取结果集的元数据信息
			System.out.println("结果集中列的数量:" + rsmd.getColumnCount());
			for(int i = 1 ; i <=rsmd.getColumnCount() ;i++) {
				String columnName = rsmd.getColumnName(i);
				String columnLable = rsmd.getColumnLabel(i);
				String columnType = rsmd.getColumnTypeName(i);
				int columnSize = rsmd.getColumnDisplaySize(i);
				System.out.println(columnName +"--"+columnLable+"--"+columnType+"--"+columnSize);
			}

在这里插入图片描述
查询代码的重构

//查询单个对象
	public <T> T getObject(String sql, Class<T> clazz) {
		// 第一步加载配置文件
		InputStream in = null;
		Connection conn = null;
		Statement stmt =null;
		ResultSet rs  = null;
		T t = null;//保存结果的对象
		try {
			in = this.getClass().getClassLoader()
					.getSystemResourceAsStream("jdbc.properties");
			Properties pro = new Properties();
			pro.load(in);
			// 定义URL
			String url = pro.getProperty("mysql.url");
			// 创建driver
			String driverStr = pro.getProperty("mysql.driver");// Driver类的全类名
			// 创建用户名和密码
			String user = pro.getProperty("mysql.user");
			String password = pro.getProperty("mysql.password");
			// 加载mysql的Dirver
			Class.forName(driverStr);
			conn = DriverManager.getConnection(url, user, password);
			stmt = conn.createStatement();
		    rs =	stmt.executeQuery(sql);
		    ResultSetMetaData rsmd = rs.getMetaData();
		   //使用反射机制 来创建对象
		    while(rs.next()) {
		    	t = clazz.newInstance();
		    	for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
			    	String label = rsmd.getColumnLabel(i);
			    	Field field = clazz.getDeclaredField(label);//使用别名来构建对象
			    	field.setAccessible(true);
			    	field.set(t, rs.getObject(i));  	
			    }
		    }
		    
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

		return t;
	}

解决试题中的属性名称和数据库的字段名程不一致的情况:可以使用数据库字段的别名来解决

//查询多个对象  返回集合
	public <T> List<T> getObjectList(String sql, Class<T> clazz) {
		// 第一步加载配置文件
		InputStream in = null;
		Connection conn = null;
		Statement stmt =null;
		ResultSet rs  = null;
		List<T> list = new ArrayList<>();
		T t = null;//保存结果的对象
		try {
			in = this.getClass().getClassLoader()
					.getSystemResourceAsStream("jdbc.properties");
			Properties pro = new Properties();
			pro.load(in);
			// 定义URL
			String url = pro.getProperty("mysql.url");
			// 创建driver
			String driverStr = pro.getProperty("mysql.driver");// Driver类的全类名
			// 创建用户名和密码
			String user = pro.getProperty("mysql.user");
			String password = pro.getProperty("mysql.password");
			// 加载mysql的Dirver
			Class.forName(driverStr);
			conn = DriverManager.getConnection(url, user, password);
			stmt = conn.createStatement();
		    rs =	stmt.executeQuery(sql);
		    ResultSetMetaData rsmd = rs.getMetaData();
		   //使用反射机制 来创建对象
		    while(rs.next()) {
		    	t = clazz.newInstance();
		    	for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
			    	String label = rsmd.getColumnLabel(i);
			    	Field field = clazz.getDeclaredField(label);//使用别名来构建对象
			    	field.setAccessible(true);
			    	field.set(t, rs.getObject(i));  	
			    }
		    	list.add(t);
		    }
		    
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

		return list;
	}

资源关闭 异常处理:

//查询多个对象  返回集合
	public <T> List<T> getObjectList(String sql, Class<T> clazz) {
		// 第一步加载配置文件
		InputStream in = null;
		Connection conn = null;
		Statement stmt =null;
		ResultSet rs  = null;
		List<T> list = new ArrayList<>();
		T t = null;//保存结果的对象
		try {
			in = this.getClass().getClassLoader()
					.getSystemResourceAsStream("jdbc.properties");
			Properties pro = new Properties();
			pro.load(in);
			// 定义URL
			String url = pro.getProperty("mysql.url");
			// 创建driver
			String driverStr = pro.getProperty("mysql.driver");// Driver类的全类名
			// 创建用户名和密码
			String user = pro.getProperty("mysql.user");
			String password = pro.getProperty("mysql.password");
			// 加载mysql的Dirver
			Class.forName(driverStr);
			conn = DriverManager.getConnection(url, user, password);
			stmt = conn.createStatement();
		    rs =	stmt.executeQuery(sql);
		    ResultSetMetaData rsmd = rs.getMetaData();
		   //使用反射机制 来创建对象
		    while(rs.next()) {
		    	t = clazz.newInstance();
		    	for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
			    	String label = rsmd.getColumnLabel(i);
			    	Field field = clazz.getDeclaredField(label);//使用别名来构建对象
			    	field.setAccessible(true);
			    	field.set(t, rs.getObject(i));  	
			    }
		    	list.add(t);
		    }
		    
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(rs !=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally {
					if(stmt !=null) {
						try {
							stmt.close();
						} catch (SQLException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}finally {
							if(conn !=null) {
								try {
									conn.close();
								} catch (SQLException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}finally {
									if(in!=null) {
										try {
											in.close();
										} catch (IOException e) {
											// TODO Auto-generated catch block
											e.printStackTrace();
										}
									}
								}
							}
						}
					}
				}
			}
		}
		

		return list;
	}

代码的再次重构
1 将获取数据库链接及释放链接的方法 提炼出一个工具类
①获取连接以及释放资源的工具类

package org.lanqiao.utils;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class JDBCUtil {
	// 获取链接
	public  static  Connection getConnnection() {
		// 第一步加载配置文件
		InputStream in = null;
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try {
			in = JDBCUtil.Class.getClassLoader().getSystemResourceAsStream("jdbc.properties");
			Properties pro = new Properties();
			pro.load(in);
			// 获取链接属性
			String url = pro.getProperty("mysql.url");
			String driverStr = pro.getProperty("mysql.driver");// Driver类的全类名
			String user = pro.getProperty("mysql.user");
			String password = pro.getProperty("mysql.password");
			// 加载mysql的Dirver
			Class.forName(driverStr);
			conn = DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return conn;
	}
	// 释放资源
	public static void  releaseSource(ResultSet rs,Statement stmt,Connection conn) {
		if(rs !=null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally {
				if(stmt !=null) {
					try {
						stmt.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}finally {
						if(conn !=null) {
							try {
								conn.close();
							} catch (SQLException e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
	}
}

②结果处理的工具类

package org.lanqiao.utils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class ResultHandler {
	public static  <T> T getObject(String sql, Class<T> clazz) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		T t = null;
		try {
			 conn = JDBCUtil.getConnection();
			 stmt  = conn.createStatement();
			  rs = stmt.executeQuery(sql);
			  ResultSetMetaData rsmd = rs.getMetaData();
			  while(rs.next()) {
				  t = clazz.newInstance();
				  for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
					  String label = rsmd.getColumnLabel(i);
					  Field field = clazz.getDeclaredField(label);
					  field.setAccessible(true);
					  field.set(t, rs.getObject(i));
				  }
			  }
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return t;
		
		
	}
	//查询多个对象  返回集合
	public static <T> List<T> getObjectList(String sql, Class<T> clazz) {
		// 第一步加载配置文件
		
		Connection conn = null;
		Statement stmt =null;
		ResultSet rs  = null;
		List<T> list = new ArrayList<>();
		T t = null;//保存结果的对象
		try {
		conn = JDBCUtil.getConnection();
			stmt = conn.createStatement();
			//设置SQL语句的参数
			
		    rs =	stmt.executeQuery(sql);
		    ResultSetMetaData rsmd = rs.getMetaData();
		   //使用反射机制 来创建对象
		    while(rs.next()) {
		    	t = clazz.newInstance();
		    	for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
			    	String label = rsmd.getColumnLabel(i);
			    	Field field = clazz.getDeclaredField(label);//使用别名来构建对象
			    	field.setAccessible(true);
			    	field.set(t, rs.getObject(i));  	
			    }
		    	list.add(t);
		    }
		    
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			 JDBCUtil.releaseSource(rs, stmt, conn);
		}
		

		return list;
	}
}

2.PreparedStatement的使用

在这里插入图片描述
解决SQL注入的问题
**Statement :**用于执行静态SQL语句并返回其生成的结果的对象。
**PreparedStatement:**表示预编译的SQL语句的对象。
SQL语句已预编译并存储在PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。 就是解决SQL注入的根本
通用结果处理的工具类

package org.lanqiao.utils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class ResultHandler2 {
	//查询单个对象
		public static  <T> T getObject(String sql, Class<T> clazz,Object ... objs) {
			// 第一步加载配置文件
			Connection conn = null;
			PreparedStatement pstmt =null;
			ResultSet rs  = null;
			T t = null;//保存结果的对象
			try {
				conn= JDBCUtil.getConnection();
				pstmt = conn.prepareStatement(sql);
				//设置SQL语句的参数
				for(int i =0 ; i < objs.length;i++) {
					pstmt.setObject(i+1, objs[i]);
				}
			    rs =	pstmt.executeQuery();
			    ResultSetMetaData rsmd = rs.getMetaData();
			   //使用反射机制 来创建对象
			    while(rs.next()) {
			    	t = clazz.newInstance();
			    	for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
				    	String label = rsmd.getColumnLabel(i);
				    	Field field = clazz.getDeclaredField(label);//使用别名来构建对象
				    	field.setAccessible(true);
				    	field.set(t, rs.getObject(i));  	
				    }
			    }
			    
				
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				JDBCUtil.releaseSource(rs, pstmt, conn);
			}
			

			return t;
		}
	


	//查询多个对象  返回集合
		public static <T> List<T> getObjectList(String sql, Class<T> clazz,Object ...objs) {
			// 第一步加载配置文件
			
			Connection conn = null;
			PreparedStatement pstmt =null;
			ResultSet rs  = null;
			List<T> list = new ArrayList<>();
			T t = null;//保存结果的对象
			try {
			conn = JDBCUtil.getConnection();
				pstmt = conn.prepareStatement(sql);
				//设置SQL语句的参数
				for(int i =0 ; i < objs.length;i++) {
					pstmt.setObject(i+1, objs[i]);
				}
			    rs =	pstmt.executeQuery();
			    ResultSetMetaData rsmd = rs.getMetaData();
			   //使用反射机制 来创建对象
			    while(rs.next()) {
			    	t = clazz.newInstance();
			    	for(int i = 1 ; i <= rsmd.getColumnCount();i++) {
				    	String label = rsmd.getColumnLabel(i);
				    	Field field = clazz.getDeclaredField(label);//使用别名来构建对象
				    	field.setAccessible(true);
				    	field.set(t, rs.getObject(i));  	
				    }
			    	list.add(t);
			    }
			    
				
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				 JDBCUtil.releaseSource(rs, pstmt, conn);
			}
			

			return list;
		}
}

测试

@Test
	public void  insertTest() throws SQLException {
		Connection conn = JDBCUtil.getConnection();
		String sql = "insert into emp(ename,job,hiredate,sal)values(?,?,?,?)";//?是占位符
		PreparedStatement pstmt = conn.prepareStatement(sql);
		//设置参数
		pstmt.setString(1, "张小三");
		pstmt.setString(2, "程序员");
		pstmt.setDate(3, new Date(new java.util.Date().getTime()));
		pstmt.setDouble(4, 6000.00);
		//执行sql语句
		int res = pstmt.executeUpdate();
		
		System.out.println(res);
		
	}
	
	@Test
	public void  queryTest() throws SQLException {
		Connection conn = JDBCUtil.getConnection();
		String sql = "select uid,username,password,realname realName,age,birthday from users where username=? and password=?";//?是占位符
		Users user = ResultHandler2.getObject(sql, Users.class, "admin","123456");
		System.out.println(user);
		 
	}

针对模糊查询的sql的写法
第一种

@Test
	public void  likeQueryTest() throws SQLException {
		Connection conn = JDBCUtil.getConnection();
		String sql = "select uid,username,password,realname realName,age,birthday from users where username like  concat('%',?,'%')";//?是占位符
		List<Users> list = ResultHandler2.getObjectList(sql, Users.class, "s");
		for(Users user : list) {
			System.out.println(user);
		}
		 
	}

第二种

@Test
	public void  likeQueryTest() throws SQLException {
		Connection conn = JDBCUtil.getConnection();
		String sql = "select uid,username,password,realname realName,age,birthday from users where username like ?";//?是占位符
		List<Users> list = ResultHandler2.getObjectList(sql, Users.class, "%s%");
		for(Users user : list) {
			System.out.println(user);
		}
		 
	}

可以有效防止SQL注入

小结:

JDBC:实现数据的持久化
JDBC是Java所提供的一套访问数据库的接口(规范)
接口都位于java.sql包下
接口有数据库厂商实现 --数据库的驱动包
JDBC的使用步骤:

  1. 获取数据库的驱动包
  2. 将数据库的驱动包导入到项目中(类路径下)
  3. 配置连接属性
    jdbc.properties
url:jdbc:mysql://localhost:3306/databaseName
driver:
com.mysql.jdbc.Driver/oracle.jdbc.dirver.OracleDriver
user:username
password:password 
  1. 加载(注册)驱动 class.forName(驱动的全类名);
  2. 获取链接:
Connection  conn =DriverManager.getconnection(url,userr.password);
  1. 获取执行对象
    Statement/PreparedStatement
Statement stmt = conn.createStatement();
PreparedStatement  pstmt = conn.preparedStatement()

Statement /PreparedStatement 区别
Statement :执行静态sql (sql是通过拼串的形式拼接出来的) 存在SQL注入的风险
PreparedStatement :执行预编译sql 可以有效的预防SQL注入

  1. 执行sql
调用stmt/pstmt的executeUpdatet()/ executeQuery()
  1. 如果执行的是查询操作 则需要进行结果集的处理
    ResultSet表示结果集
    next()判断是否有记录
    getXXX(index/name)获取相应的记录的值
    ResultSetMetaData使用它可以获取结果集对象的一些描述信息
getColumnCount()
getColumnName()
getColumnLable()
getColumnType()
  1. 释放资源

四、 操作BLOB类型字段

数据库存储中一些特殊数据:

大文本数据    text(mysql)  字符类型   clob(oralce)
大二进制数据  blob   二进制数据
@Test
	public void insetBlob() throws SQLException, FileNotFoundException {
		//获取链接
		Connection conn = JDBCUtil.getConnection();
		String sql = "insert into users(username,password,realname,age,birthday,photo) values(?,?,?,?,?,?)";
		//获取执行对象
		PreparedStatement pstmt = conn.prepareStatement(sql);
		//设置参数
		pstmt.setString(1, "jack");
		pstmt.setString(2,"123456");
		pstmt.setString(3, "小五");
		pstmt.setInt(4, 23);
		pstmt.setDate(5, new Date(new java.util.Date().getTime()));
		InputStream in = new FileInputStream(new File("src/l.jpg"));
		pstmt.setBlob(6, in);
		//执行sql
		pstmt.executeUpdate();
		//释放资源
		JDBCUtil.releaseSource(null, pstmt, conn);	
	}
	//查询Blob
	@Test
	public void queryBlob() throws SQLException, IOException {
		//获取链接
		Connection conn = JDBCUtil.getConnection();
		String sql = "select photo from users where uid=?";
		//获取执行对象
		PreparedStatement pstmt = conn.prepareStatement(sql);
		//设置参数
		pstmt.setInt(1, 5);
		//执行sql
		ResultSet rs = pstmt.executeQuery();
		while(rs.next()) {
			Blob photo = rs.getBlob("photo");
			InputStream in = photo.getBinaryStream();
			OutputStream out = new FileOutputStream(new File("src/123.jpg"));
			byte[] buf = new byte[1024];
			int len =0;
			while((len = in.read(buf))!= -1) {
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
		}
		
		//释放资源
		JDBCUtil.releaseSource(rs, pstmt, conn);	
	}

虽然数据库提供了对于大数据的存储和访问技术 但是在实际开发中并不会这样使用。这样使用除非是有特殊要求。
开发中一般的做法:

 不会在数据库中直接保存文件数据 而只是在数据库中保存文件的存储路径就可以了。

Text 文本数据(练习)

五、CallableStatement (在程序中来调用存储过程和函数)

存储过程和函数的区别:返回值

public interface CallableStatement  extends PreparedStatement

用于执行SQL存储过程的界面。 JDBC API提供了存储过程SQL转义语法,允许以标准方式为所有RDBMS调用存储过程。
此转义语法包含一个结果参数和不包含结果参数的表单。 如果使用,结果参数必须注册为OUT参数。 其他参数可用于输入,输出或两者。
参数按顺序依次引用,第一个参数为1。

{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
   {call <procedure-name>[(<arg1>,<arg2>, ...)]} 

在这里插入图片描述

//调用存储过程
	@Test
	public void proTest() throws SQLException {
		//获取链接
		Connection conn = JDBCUtil.getConnection();
		//获取执行对象
		String sql = "{call pro1(?,?)}";
		CallableStatement cs = conn.prepareCall(sql);
		//设置参数
		cs.setInt(1, 20);
		//注册输出参数
		cs.registerOutParameter(2,JDBCType.INTEGER);
		//执行存储过程
		cs.execute();
		int num = cs.getInt(2);
		System.out.println(num);
		JDBCUtil.releaseSource(null, cs, conn);
	}
//调用函数
		@Test
		public void funTest() throws SQLException {
			//获取链接
			Connection conn = JDBCUtil.getConnection();
			//获取执行对象  调用函数 第一个问好 表示返回值  第二个问号  表示输入参数
			String sql = "{?=call fun(?)}";
			CallableStatement cs = conn.prepareCall(sql);
			//设置参数
			cs.setInt(2, 10);
			//注册输出参数
			cs.registerOutParameter(1,JDBCType.INTEGER);
			//执行存储过程
			cs.execute();
			int num = cs.getInt(1);//获取返回值或者输出参数的值
			System.out.println(num);
			JDBCUtil.releaseSource(null, cs, conn);
		}

存储过程和函数 相较于普通sql 那么他们的优劣
从执行过程:

  1. 函数和存储过程 是存储在数据库服务端的
    普通sql是在程序中 执行的时候 才会发送给数据库服务端
  2. 函数和存储过程在服务端 没有发送的过程 所以效率高
    普通sql需要将sql语句发送到服务端 所以效率低
  3. 将简单sql编写在程序中
    复杂的sql 则可以通过存储过程或函数来实现。可以简化开发人员对sql 的复杂操作

六、 批量插入(了解)

1 批量执行SQL语句

当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
JDBC的批量处理语句包括下面三个方法:

  1. addBatch(String):添加需要批量处理的SQL语句或是参数;
  2. executeBatch():执行批量处理语句;
  3. clearBatch():清空缓存的数据
    通常我们会遇到两种批量执行SQL语句的情况:
  4. 多条SQL语句的批量处理;
  5. 一个SQL语句的批量传参;

2 高效的批量插入

举例:向数据表中插入20000条数据
数据库中提供一个goods表。创建如下:

CREATE TABLE goods(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);

2.1 实现层次一:使用Statement

Connection conn = JDBCUtils.getConnection();
Statement st = conn.createStatement();
for(int i = 1;i <= 20000;i++){
	String sql = "insert into goods(name) values('name_' + "+ i +")";
	st.executeUpdate(sql);
}

2.2 实现层次二:使用PreparedStatement

long start = System.currentTimeMillis();
		
Connection conn = JDBCUtils.getConnection();
		
String sql = "insert into goods(name)values(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for(int i = 1;i <= 20000;i++){
	ps.setString(1, "name_" + i);
	ps.executeUpdate();
}
		
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//82340
		
		
JDBCUtils.closeResource(conn, ps);

2.3 实现层次三

/*
 * 修改1: 使用 addBatch() / executeBatch() / clearBatch()
 * 修改2:mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
 * 		 ?rewriteBatchedStatements=true 写在配置文件的url后面
 * 修改3:使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar
 * 
 */
@Test
public void testInsert1() throws Exception{
	long start = System.currentTimeMillis();
		
	Connection conn = JDBCUtils.getConnection();
		
	String sql = "insert into goods(name)values(?)";
	PreparedStatement ps = conn.prepareStatement(sql);
		
	for(int i = 1;i <= 1000000;i++){
		ps.setString(1, "name_" + i);
			
		//1.“攒”sql
		ps.addBatch();
		if(i % 500 == 0){
			//2.执行
			ps.executeBatch();
			//3.清空
			ps.clearBatch();
		}
	}
		
	long end = System.currentTimeMillis();
	System.out.println("花费的时间为:" + (end - start));//20000条:625                                                                         //1000000条:14733  
		
	JDBCUtils.closeResource(conn, ps);
}

2.4 实现层次四

/*
* 层次四:在层次三的基础上操作
* 使用Connection 的 setAutoCommit(false)  /  commit()
*/
@Test
public void testInsert2() throws Exception{
	long start = System.currentTimeMillis();
		
	Connection conn = JDBCUtils.getConnection();
		
	//1.设置为不自动提交数据
	conn.setAutoCommit(false);
		
	String sql = "insert into goods(name)values(?)";
	PreparedStatement ps = conn.prepareStatement(sql);
		
	for(int i = 1;i <= 1000000;i++){
		ps.setString(1, "name_" + i);
			
		//1.“攒”sql
		ps.addBatch();
			
		if(i % 500 == 0){
			//2.执行
			ps.executeBatch();
			//3.清空
			ps.clearBatch();
		}
	}
		
	//2.提交数据
	conn.commit();
		
	long end = System.currentTimeMillis();
	System.out.println("花费的时间为:" + (end - start));//1000000条:4978 
		
	JDBCUtils.closeResource(conn, ps);
}

随堂练习链接:

随堂练习

发布了67 篇原创文章 · 获赞 6 · 访问量 1920

猜你喜欢

转载自blog.csdn.net/weixin_45801537/article/details/104588708