一、JDBC: 1.概念 Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
2.快速入门 步骤:
(1) 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar(==不同的Java版本导入不同的jar包==)
1 2 1. 复制mysql-connector-java-5.1 .37 -bin.jar到项目的libs目录下2. 右键-->Add As Library
(2) 注册驱动
1 Class.forName("com.mysql.jdbc.Driver" ); java-5.1 .37
(3) 获取数据库连接对象 Connection
1 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3" , "root" , "root" );
(4) 定义sql
1 String sql = "update account set balance = 500 where id = 1" ;
(5) 获取执行sql语句的对象 Statement
1 Statement stmt = conn.createStatement();
(6) 执行sql,接受返回结果
1 int count = stmt.executeUpdate(sql);
(7) 处理结果
1 System.out.println(count);
(8) 释放资源 注意空指针异常
1 2 stmt.close(); 先释放 conn.close();
3.详解各个对象 (1) DriverManager:驱动管理对象 功能:
①.注册驱动:告诉程序该使用哪一个数据库驱动jar
1 2 3 4 5 6 7 8 9 10 static void registerDriver (Driver driver) :注册与给定的驱动程序 DriverManager 。 写代码使用: Class.forName("com.mysql.jdbc.Driver" ); 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块 static { try { java.sql.DriverManager.registerDriver(new Driver ()); } catch (SQLException E) { throw new RuntimeException ("Can't register driver!" ); } }
注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
②.获取数据库连接:
方法:
1 static Connection getConnection (String url, String user, String password)
参数:
1 2 3 url:指定连接的路径 user:用户名 password:密码
1 2 语法:jdbc:mysql: 例子:jdbc:mysql:
如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
(2) Connection:数据库连接对象 功能:
① 获取执行sql 的对象
1 2 Statement createStatement () PreparedStatement prepareStatement (String sql)
② 管理事务:
1 2 3 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false ,即开启事务 提交事务:commit() 回滚事务:rollback()
(3) Statement:执行sql的对象 ① 执行sql
1 2 3 4 5 6 boolean execute (String sql) :可以执行任意的sql 不常用int executeUpdate (String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0 的则执行成功,反之,则失败。 ResultSet executeQuery (String sql) :执行DQL(select)语句
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Statement stmt = null ; Connection conn = null ; try { Class.forName("com.mysql.jdbc.Driver" ); String sql = "insert into account values(null,'王五',3000)" ; conn = DriverManager.getConnection("jdbc:mysql:///db3" , "root" , "root" ); stmt = conn.createStatement(); int count = stmt.executeUpdate(sql); System.out.println(count); if (count > 0 ){ System.out.println("添加成功!" ); }else { System.out.println("添加失败!" ); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (stmt != null ){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null ){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
② ResultSet:结果集对象,封装查询结果
方法:
boolean next()
1 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false ,如果不是则返回true
getXxx(参数):获取数据
1 Xxx:代表数据类型 如: int getInt () , String getString ()
1 2 3 参数: int :代表列的编号,从1 开始 如: getString(1 )String:代表列名称。 如: getDouble("balance" )
使用步骤:
1 2 3 1. 游标向下移动一行2. 判断是否有数据3. 获取数据
1 2 3 4 5 6 7 8 9 while (rs.next()){ int id = rs.getInt(1 ); String name = rs.getString("name" ); double balance = rs.getDouble(3 ); System.out.println(id + "---" + name + "---" + balance); }
(4) PreparedStatement:执行sql的对象 SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
1 2 比如:输入用户随便,输入密码:a' or ' a' = ' a sql :select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a'
解决sql注入问题:使用PreparedStatement对象来解决
==预编译的SQL==:参数使用?作为占位符
步骤:
导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
注册驱动
获取数据库连接对象 Connection
==定义sql==
1 2 注意:sql的参数使用?作为占位符 如:select * from user where username = ? and password = ?;
==获取执行sql语句的对象 PreparedStatement==
1 Connection.prepareStatement(String sql)
==给?赋值==:
1 2 3 方法: setXxx(参数1,参数2) * 参数1:?的位置编号 从1 开始 * 参数2:?的值
执行sql,接受返回结果,不需要传递sql语句
处理结果
释放资源
例如:
1 2 3 4 5 6 7 8 9 String sql = "select * from users where usernames =? and passwords =?" ; PreparedStatement preparedStatement = con.prepareStatement(sql); preparedStatement.setString(1 ,username); preparedStatement.setString(2 ,password); System.out.println(sql); ResultSet resultSet = preparedStatement.executeQuery();
==后期都会使用PreparedStatement来完成增删改查的所有操作==,好处:
4.抽取JDBC工具类 : JDBCUtils 目的:简化书写
分析:
1 2 3 4 5 6 7 8 1. 注册驱动也抽取2. 抽取一个方法获取连接对象 * 需求:不想传递参数(麻烦),还得保证工具类的通用性。 * 解决:配置文件 jdbc.properties url= user= password=
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 3. 抽取一个方法释放资源* 代码实现: public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; static { try { Properties pro = new Properties (); ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties" ); String path = res.getPath(); System.out.println(path); pro.load(new FileReader (path)); url = pro.getProperty("url" ); user = pro.getProperty("user" ); password = pro.getProperty("password" ); driver = pro.getProperty("driver" ); Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection () throws SQLException { return DriverManager.getConnection(url, user, password); } public static void close (Statement stmt,Connection conn) { if ( stmt != null ){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( conn != null ){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
对于ResultSet结果集对象,释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static void close (ResultSet rs,Statement stmt, Connection conn) { if ( rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( stmt != null ){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if ( conn != null ){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
例子
需求:
(1) 通过键盘录入用户名和密码
(2) 判断用户是否登录成功
1 2 select * from user where username = "" and password = "" ; 如果这个sql有查询结果,则成功,反之,则失败
步骤:
(1) 创建数据库表 user
1 2 3 4 5 6 7 8 CREATE TABLE USER ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32 ) , PASSWORD VARCHAR (32 ) ); INSERT INTO USER VALUES (NULL,'zhangsan' ,'123' ) ; INSERT INTO USER VALUES (NULL,'lisi' ,'234' ) ;
(2) 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class JDBCDemo9 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入用户名:" ); String username = sc.nextLine(); System.out.println("请输入密码:" ); String password = sc.nextLine(); boolean flag = new JDBCDemo9 ().login(username, password); if (flag){ System.out.println("登录成功!" ); }else { System.out.println("用户名或密码错误!" ); } }
登录方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public boolean login (String username ,String password) { if (username == null || password == null ){ return false ; } Connection conn = null ; Statement stmt = null ; ResultSet rs = null ; try { conn = JDBCUtils.getConnection(); String sql = "select * from user where username = '" +username+"' and password = '" +password+"' " ; stmt = conn.createStatement(); rs = stmt.executeQuery(sql); return rs.next(); } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtils.close(rs,stmt,conn); } return false ; } }
5.JDBC控制事务 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作:
使用Connection对象来管理事务
1 2 3 4 5 6 * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false ,即开启事务 * 在执行sql之前开启事务 * 提交事务:commit() * 当所有sql都执行完提交事务 * 回滚事务:rollback() * 在catch 中回滚事务
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class JDBCDemo10 { public static void main (String[] args) { Connection conn = null ; PreparedStatement pstmt1 = null ; PreparedStatement pstmt2 = null ; try { conn = JDBCUtils.getConnection(); conn.setAutoCommit(false ); String sql1 = "update account set balance = balance - ? where id = ?" ; String sql2 = "update account set balance = balance + ? where id = ?" ; pstmt1 = conn.prepareStatement(sql1); pstmt2 = conn.prepareStatement(sql2); pstmt1.setDouble(1 ,500 ); pstmt1.setInt(2 ,1 ); pstmt2.setDouble(1 ,500 ); pstmt2.setInt(2 ,2 ); pstmt1.executeUpdate(); int i = 3 /0 ; pstmt2.executeUpdate(); conn.commit(); } catch (Exception e) { try { if (conn != null ) { conn.rollback(); } } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { JDBCUtils.close(pstmt1,conn); JDBCUtils.close(pstmt2,null ); } } }
6.数据库连接池 概念:其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
好处:
实现方式
标准接口:DataSource javax.sql包下的,sun公司并没有实现具体的方法,而是交给了提供驱动的数据厂商来实现
1 2 1.C3P0技术:数据库连接池技术 2.Druid:由阿里提供的数据库连接池技术
方法:
1 2 3 获取连接:getConnection() 归还连接:Connection.close() 如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
(1) C3P0数据库连接池技术 步骤:
导入jar包 (两个) 项目下libs
1 2 3 c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar mysql-connector-java-5.1.37-bin.jar 不要忘记导入数据库驱动jar包!!!!!!
定义配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 名称: c3p0.properties 或者 c3p0-config.xml 路径: 直接将文件放在src目录下即可。 <default-config> <!-- 连接参数 --> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/db3</property> <property name="user">root</property> <property name="password">root</property> <!-- 连接池参数 --> <property name="initialPoolSize">5</property> <property name="maxPoolSize">10</property> <property name="checkoutTimeout">3000</property> </default-config> 采用有参构造! <named-config name="otherc3p0"> .............. </named-config>
定义配置文件:创建核心对象 数据库连接池对象 ComboPooledDataSource
1 2 //创建数据库连接池对象 DataSource ds = new ComboPooledDataSource();
获取连接: getConnection
1 2 //获取连接对象 Connection conn = ds.getConnection();
(2) Druid数据库连接池技术 步骤:
导入jar包
1 2 druid-1.0.9.jar 项目下libs mysql-connector-java-5.1.37-bin.jar 不要忘记导入数据库驱动jar包!!!!!!
定义配置文件:
1 2 是properties形式的 可以叫任意名称,可以放在任意目录下,需要手动加载
加载配置文件 Properties
1 2 3 4 //3.加载配置文件 Properties pro = new Properties(); InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is);
获取数据库连接池对象
1 2 3 通过工厂来来获取 DruidDataSourceFactory //4.获取连接池对象 DataSource ds = DruidDataSourceFactory.createDataSource(pro);
获取连接
1 2 3 getConnection //5.获取连接 Connection conn = ds.getConnection();
(3) Druid工具类 JDBCUtils工具类
提供静态代码块加载配置文件,初始化连接池对象
提供方法
1 2 3 获取连接方法:通过数据库连接池获取连接 获取连接池的方法 释放资源
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class JDBCUtils { private static DataSource ds ; static { try { Properties pro = new Properties (); pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties" )); ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () throws SQLException { return ds.getConnection(); } public static void close (Statement stmt,Connection conn) { close(null ,stmt,conn); } public static void close (ResultSet rs , Statement stmt, Connection conn) { if (rs != null ){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null ){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null ){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static DataSource getDataSource () { return ds; } }
使用工具类
1 2 3 4 Connection conn = JDBCUtils.getConnection(); String sql = "select * from employee" ; PreparedStatement preparedStatement = conn.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery();
7.Spring JDBC Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
步骤:
导入jar包
1 2 3 4 5 6 7 8 9 五个Spring jar包 spring-tx-5.2.16.RELEASE.jar spring-jdbc-5.2.16.RELEASE.jar spring-jcl-5.2.16.RELEASE.jar spring-core-5.2.16.RELEASE.jar spring-beans-5.2.16.RELEASE.jar 两个数据库连接池jar包 druid-1.2.8.jar mysql-connector-java-8.0.27.jar
创建JdbcTemplate对象。依赖于数据源DataSource
1 2 3 4 ds用的上边JDBCUtils的方法,其中 ds为DataSource 用到了JDBCUtils ,所以要有配置文件 JdbcTemplate template = new JdbcTemplate(ds);
调用JdbcTemplate的方法来完成CRUD的操作
① update():
② queryForMap():
1 2 查询结果将结果集封装为map集合,将列名作为key,将值作为value.将这条记录封装为一个map集合 注意:这个方法查询的结果集长度只能是1
③ queryForList():
1 2 查询结果将结果集封装为list集合 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
④ query():
1 2 3 4 查询结果,将结果封装为JavaBean对象 query的参数:RowMapper 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装 new BeanPropertyRowMapper<类型>(类型.class)
queryForObject:
1 2 查询结果,将结果封装为对象 * 一般用于聚合函数的查询
例如:
1 2 3 4 5 6 7 修改1号数据的 salary 为 10000 添加一条记录 删除刚才添加的记录 查询id为1的记录,将其封装为Map集合 查询所有记录,将其封装为List 查询所有记录,将其封装为Emp对象的List集合 查询总记录数
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 import cn.itcast.domain.Emp;import cn.itcast.utils.JDBCUtils;import org.junit.Test;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import java.sql.Date;import java.sql.ResultSet;import java.sql.SQLException;import java.util.List;import java.util.Map;public class JdbcTemplateDemo2 { private JdbcTemplate template = new JdbcTemplate (JDBCUtils.getDataSource()); @Test public void test1 () { String sql = "update emp set salary = 10000 where id = 1001" ; int count = template.update(sql); System.out.println(count); } @Test public void test1 () { String sql = "update emp set salary = 10000 where id = 1001" ; int count = template.update(sql); System.out.println(count); } @Test public void test2 () { String sql = "insert into emp(id,ename,dept_id) values(?,?,?)" ; int count = template.update(sql, 1015 , "郭靖" , 10 ); System.out.println(count); } @Test public void test3 () { String sql = "delete from emp where id = ?" ; int count = template.update(sql, 1015 ); System.out.println(count); } @Test public void test4 () { String sql = "select * from emp where id = ? or id = ?" ; Map<String, Object> map = template.queryForMap(sql, 1001 ,1002 ); System.out.println(map); } @Test public void test5 () { String sql = "select * from emp" ; List<Map<String, Object>> list = template.queryForList(sql); for (Map<String, Object> stringObjectMap : list) { System.out.println(stringObjectMap); } } @Test public void test6 () { String sql = "select * from emp" ; List<Emp> list = template.query(sql, new RowMapper <Emp>() { @Override public Emp mapRow (ResultSet rs, int i) throws SQLException { Emp emp = new Emp (); int id = rs.getInt("id" ); String ename = rs.getString("ename" ); int job_id = rs.getInt("job_id" ); int mgr = rs.getInt("mgr" ); Date joindate = rs.getDate("joindate" ); double salary = rs.getDouble("salary" ); double bonus = rs.getDouble("bonus" ); int dept_id = rs.getInt("dept_id" ); emp.setId(id); emp.setEname(ename); emp.setJob_id(job_id); emp.setMgr(mgr); emp.setJoindate(joindate); emp.setSalary(salary); emp.setBonus(bonus); emp.setDept_id(dept_id); return emp; } }); for (Emp emp : list) { System.out.println(emp); } } @Test public void test6_2 () { String sql = "select * from emp" ; List<Emp> list = template.query(sql, new BeanPropertyRowMapper <Emp>(Emp.class)); for (Emp emp : list) { System.out.println(emp); } } @Test public void test7 () { String sql = "select count(id) from emp" ; Long total = template.queryForObject(sql, Long.class); System.out.println(total); } }