JAVA JDBC
JDBC
JDBC概述
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库! 每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
概念:Java语言操作数据库
本质:官网定义的一套操作所有关系型数据库的规则(接口)。各个数据库厂商来实现这套接口,提供数据库驱动包(jar包)。我们使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
导入mysql驱动包
开发步骤
1.注册驱动:告知JVM使用的是哪一个数据库的驱动
2.获得连接:使用JDBC中的类,完成对MySQL数据库的连接
3.获得语句执行平台:通过连接对象获取对SQL语句的执行者对象
4.执行sql语句:使用执行者对象,向数据库执行SQL语句 获取到数据库的执行后的结果
5.处理结果
6.释放资源:调用一堆close()方法.
public static void main(String[] args) throws SQLException, ClassNotFoundException { //注册驱动 Class.forName("com.mysql.jdbc.Driver"); //获得连接 String url = "jdbc:mysql://localhost:3306/study"; String username = "wdnmd"; String password = "123"; Connection connection = DriverManager.getConnection(url, username, password); //获得语句执行平台 Statement statement = connection.createStatement(); //执行sql语句 String sql = "select * from sort";
//处理结果 ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()){ int sid = resultSet.getInt("sid"); String sname = resultSet.getString("sname"); double sprice = resultSet.getDouble("sprice"); String sdesc = resultSet.getString("sdesc"); System.out.println(sid +"-"+sname +"-"+ sprice +"-"+ sdesc); } //释放资源 resultSet.close(); statement.close(); connection.close(); }
查询用executeQuery 增删改用executeUpdate
JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver DriverManager工具类,提供注册驱动的方法 registerDriver(),方法的参数是java.sql.Driver,所以我们可以通过如下语句进行注册: DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 以上代码不推荐使用,存在两方面不足 1. 硬编码,后期不易于程序扩展和维护 2. 驱动被注册两次。 通常开发我们使用Class.forName() 加载一个使用字符串描述的驱动类。 如果使用Class.forName()将类加载到内存,该类的静态代码将自动执行。 通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册
JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。
第一部分是jdbc,这是固定的;
第二部分是数据库名称,那么连接mysql数据库,第二部分当然是mysql了;
第三部分是由数据库厂商规定的,我们需要了解每个数据库厂商的要求,mysql的第三部分分别由数据库服务器的IP地址(localhost)、端口号(3306),以及DATABASE名称(mydb)组成。
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,
当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
详解各个对象
DriverManager:驱动管理对象
1.注册驱动:
写代码用:Class.forName("com.mysql.jdbc.Driver"); 注册与给定的驱动程序
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static{
try{
java.sql.DriverManager.registerDriver(new Driver());
}catch(SQLExceprion E){
throw new RuntimeException("Can't register driver!");
}
}
注意:mysql5 之后的驱动jar包可以省略注册驱动的步骤
2.获取数据库连接
url:指定连接的路径
user:用户名
password:密码
Connection:数据库连接对象
1.获取执行sql的对象
2.管理事务:
提交事务:
开启事务:
回滚事务:
Statement:执行sql的对象
1.执行sql
返回值:影响的行数,可以通过这个影响的行数判断DML 语句是否执行成功 返回值>0则成功,反之失败
ResultSet:结果集对象
next() :游标向下移动一行
getXXX(参数)获取数据 xxx 代表数据类型:
PreparedStatement
假设有登录案例SQL语句如下:
SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX’ OR ‘a’=’a时,则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’;
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。
为此,我们使用PreparedStatement来解决对应的问题。使用PreparedStatement预处理对象时,建议每条sql语句所有的实际参数,都使用逗号分隔。
public static void main(String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/study"; String username = "wdnmd"; String password = "123"; Connection connection = DriverManager.getConnection(url,username,password); Scanner scanner = new Scanner(System.in); String user = scanner.nextLine(); String pass = scanner.nextLine(); String sql = "select * from users where username=? and password = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1,user); preparedStatement.setObject(2,pass); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getString("username")+resultSet.getString("password")); } resultSet.close(); preparedStatement.close(); connection.close(); }
JDBC工具类
import java.sql.*; public class JDBCutil { private JDBCutil(){} private static Connection connection; static{ try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/study"; String username = "wdnmd"; String password = "123"; connection = DriverManager.getConnection(url,username,password); } catch (Exception e) { throw new RuntimeException(e +"链接失败"); } } public static Connection getConnection(){ return connection; } //释放资源 public static void close(Connection connection, Statement statement, ResultSet resultSet){ if (resultSet !=null){ try{ resultSet.close(); }catch (SQLException e){ } } if (statement !=null){ try{ statement.close(); }catch (SQLException e){ } } if (connection !=null){ try{ connection.close(); }catch (SQLException e){ } } } public static void close(Connection connection, Statement statement){ if (statement !=null){ try{ statement.close(); }catch (SQLException e){ } } if (connection !=null){ try{ connection.close(); }catch (SQLException e){ } } } }
测试:
public static void main(String[] args) throws SQLException { Connection connection = JDBCutil.getConnection(); PreparedStatement pst = connection.prepareStatement("select sname from sort"); ResultSet resultSet = pst.executeQuery(); while(resultSet.next()){ System.out.println(resultSet.getString("sname")); } JDBCutil.close(connection,pst,resultSet); }
JDBC控制事务
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败
如果你在修改数据的时候 第一个sql和第二个sql 中间有个异常的话 第一个sql 执行了 但是第二个sql 却没执行 事务就是解决这类问题的
public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement1 = null; PreparedStatement preparedStatement2 = null; try { connection = JDBCUtils.getConnection(); connection.setAutoCommit(false); //调用方法参数这种false就开启事务 String sql = "update account set balance = balance - ? where id =?"; String sql2 = "update account set balance = balance + ? where id =?"; preparedStatement1 = connection.prepareStatement(sql); preparedStatement2 = connection.prepareStatement(sql2); preparedStatement1.setInt(1,500); preparedStatement1.setInt(2,1); preparedStatement2.setInt(1,500); preparedStatement2.setInt(2,2); preparedStatement1.executeUpdate(); int i =3/0; preparedStatement2.executeUpdate(); connection.commit();//当所有sql执行完成后提交事务 } catch (Exception e) { try { if (connection != null){ connection.rollback(); //回滚事务 } } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); }finally { JDBCUtils.close(preparedStatement1,connection); JDBCUtils.close(preparedStatement2,null); } }
数据库连接池
其实就是一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
好处:节约资源、用户访问高效
实现:
1. 标准接口:DataSource javax.sql包下的 1. 方法:
获取连接:getConnection()
归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
2. 一般我们不去实现它,有数据库厂商来实现
1. C3P0:数据库连接池技术
2. Druid:数据库连接池实现技术,由阿里巴巴提供的
C3P0:数据库连接池技术
步骤:1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,不要忘记导入数据库驱动jar包
2. 定义配置文件:
名称: c3p0.properties 或者 c3p0-config.xml
路径:直接将文件放在src目录下即可。
3. 创建核心对象 数据库连接池对象 ComboPooledDataSource
4. 获取连接: getConnection
代码:
//1.创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();
//2. 获取连接对象
Connection conn = ds.getConnection();
Druid:数据库连接池实现技术,由阿里巴巴提供的
步骤:
1. 导入jar包 druid-1.0.9.jar
2. 定义配置文件:
是properties形式的
可以叫任意名称,可以放在任意目录下
3. 加载配置文件。Properties
4. 获取数据库连接池对象:通过工厂来来获取 DruidDataSourceFactory
5. 获取连接:getConnection
代码:
//3.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn = ds.getConnection();
Druid连接池工具类
//定义database private static DataSource dataSource; static { try { //加载properties配置 Properties properties = new Properties(); properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties")); //加载database dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } //获取连接 public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } //释放资源 public static void close(Statement statement,Connection connection){ close(statement,connection); } public static void close(ResultSet resultSet,Statement statement,Connection connection){ if (resultSet != null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } //获取连接池方法 public static DataSource getDataSource(){ return dataSource; }
JdbcTemplate
Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
步骤:
1. 导入jar包
2. 创建JdbcTemplate对象。依赖于数据源DataSource
JdbcTemplate template = new JdbcTemplate(ds);
3. 调用JdbcTemplate的方法来完成CRUD的操作
update():执行DML语句。增、删、改语句
queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合
注意:这个方法查询的结果集长度只能是1
queryForList():查询结果将结果集封装为list集合
注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
query():查询结果,将结果封装为JavaBean对象
query的参数:RowMapper
一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
new BeanPropertyRowMapper<类型>(类型.class)
queryForObject:查询结果,将结果封装为对象
一般用于聚合函数的查询