JDBC介绍JDBC
(Java DataBase Connectivity)就是Java数据库连接,说白了就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。
JDBC中的核心类有
●DriverManager(驱动管理器)的作用有两个:
1)注册驱动:这可以让JDBC知道要使用的是哪个驱动;
2)获取Connection:如果可以获取到Connection,那么说明已经与数据库连接上了。
Connection对象表示连接
与数据库的通讯都是通过这个对象展开的:它最为重要的一个方法就是用来获取Statement对象;
●Statement
是用来向数据库发送SQL语句的,这样数据库就会执行发送过来的SQL语句:
1)int executeUpdate(String sql):执行更新操作(insert、update、delete等); 2)ResultSet executeQuery(String sql):执行查询操作,数据库在执行查询后会把查询结果,查询结果就是ResultSet;
ResultSet
对象表示查询结果集,只有在执行查询操作后才会有结果集的产生。结果集是一个二维的表格,有行有列。操作结果集要学习移动ResultSet内部的“行光标”,以及获取当前行上的每一列上的数据:
boolean next():使“行光标”移动到下一行,并返回移动后的行是否存在; XXX getXXX(int col):获取当前行指定列上的值,参数就是列数,列数从1开始,而不是0。
mysql数据库的驱动jar包
mysql-connector-java-5.1.13-bin.jar;
JDBC访问数据库的步骤
加载驱动 【DriverManager】
1. JDBC实际上就是定义了一系列的接口和类,集成在java.sql和javas.sql包中
2. 需要使用不同厂商提供的DriverManager来管理jdbc驱动
3. 不同的驱动是用来连接不同类型的数据库的
操作 1.添加jar包到项目中 2.使用反射原理来获取驱动 Class.forName("com.mysql.jdbc.Driver");//反射
获取Connection链接
1.使用刚刚加载好的驱动来获取管理驱动
2. 获取的俩接种有三各参数需要注意
1.url jdbc:mysql://localhost:3306/colin_mysql【咱们自己要连接的数据库的名字】 jdbc:mysql:///colin_mysql 2. user root 3. password 1234
操作:
//根据DrivateManager来调用获取链接的方法 String url = "jdbc:mysql://localhost:3306/colinemp"; url是我们数据库的地址 最后使我们要连接的数据库名
String user = "root";
String password = "1234";
Connection con = DriverManager.getConnection(url, user, password);
创建Statement ['steɪtm(ə)nt] 执行SQL语句
1. 根据刚刚创建的连接,生成一个执行sql 语句的状态对象
2. 准备好sql语句
1.增 insert 2. 删 delete 3. 改 update 使用的是executeUpdate ['eksɪkjuːt] (sql语句) 来执行的sql语句的。
返回的是一个int值【也就是这条sql语句执行后影响的数据条数
(有数据条数被影响那就说明成功,没有被影响的话那就说明失败)】
4.查询 select
使用的是executeQuery ['kwɪri](sql语句 ) 来执行的查询的sql语句。
返回的是一个ResultSet结果集。我们需要遍历来获取数据库的信息
循环判断结果集还是否存在下一条数据【如果存在就将数据库中的数据使用结果集对象来获取通过列名
(需要注意的是在关闭数据库释放资源的时候也要把结果集关闭掉)】
操作:
增删改
//statement对象用来发送sql语句 他是根据连接对象来生成的一个状态 stt = con.createStatement();//打开状态通道 String sql ="insert into dept values(50,'后勤部','天津')"; 准备一个sal语句【增删改】 //调用方法来处理语句 int i = stt.executeUpdate(sql); 逻辑判断 if(i>0){ System.out.println("添加成功"); }else{ System.out.println("添加失败"); }
查询
//statement对象用来发送sql语句 他是根据连接对象来生成的一个状态
stt = con.createStatement();
String sql = "select * from dept";
准备sql语句
// 只有在进行查询的时候我们使用的是executeQuery()
rs = stt.executeQuery(sql);
//遍历结果集 获取信息
while(rs.next()){//判断是否存在下一条数据
int id = rs.getInt("deptno");//可以写列的名字 也可以写列的下标 1
String name = rs.getString("deptname");//下标是 2
System.out.println(id+" --- "+name);
}
4. 返回ResultSet 查询结果
1.只有在查询的时候才会用到。
2. 需要通过遍历结果集来获取数据库中相应的信息
5. 关闭数据库,释放资源
1. 在关闭之前首要要做的是否空判断。
2. 在做查询时注意也需要将结果集进行关闭。
操作:
增删改 //释放资源 try { if(stt != null){ stt.close(); } if(con != null){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } 查询 //释放资源 try { if(rs!= null){ rs.close(); } if(stt != null){ stt.close(); } if(con != null){ con.close(); } 需要注意的是 rs结果集也是需要我们进行一个关闭操作的 } catch (SQLException e) { e.printStackTrace(); }
sql注入
关于sql语句中信息显示是否安全
如果我们从控制台输入信息 直接放入了sql语句中,是一种非常不安全的行为
当我们使用变量拼接sql语句的时候,会出现一种情况
直接修改sql语句 【手动拼接sql语句 改变了sql语句原来的逻辑顺序【语意】】完成了一个不需要正确密码和用户名的正确登陆得到数据信息
例如:
select * from Student where s_id="1" or 1=1 and s_sex="男" or 1=1;
这种就是直接篡改的语义,从而跳过数据库的验证,直接进行操作的方法
预编译PreparedStatement
它是Statement接口的子接口;
强大之处:
●防SQL攻击;
●提高代码的可读性、可维护性;
●提高效率
使用
1.使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定; select * from user where name = ? and pass = ?; sql语句模板【?就是占位符】
2.调用PreparedStatement的setXXX()系列方法为问号设置值
3.调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法; 建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement。
代码示例
//1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接 String url ="jdbc:mysql://localhost:3306/colinemp"; String user ="root"; String password = "1234"; con = DriverManager.getConnection(url, user, password); //3.开启通道发送sql语句 String sql = "select * from user where name =? and pass = ? "; sql语句模板 问号是占位符的意思 pstt = con.prepareStatement(sql); 得到预先状态通道 //4.键盘录入信息 Scanner input = new Scanner(System.in); System.out.println("请输入用户名:"); String name = input.next(); System.out.println("请输入用户密码:"); String pass = input.next(); //5.给问号占位符添加值 pstt.setString(1, name); pstt.setString(2, pass); //执行 rs = pstt.executeQuery(); 执行查询操作 while(rs.next()){ int i = rs.getInt("id"); String name1 = rs.getString("name"); String pass1 = rs.getString("pass"); System.out.println(i+"------"+name1+"------"+pass1); } //释放资源 try { if(rs != null){ rs.close(); } if(pstt!= null){ pstt.close(); } if(con!= null){ con.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }
使用的时候可已将重复的操作,封装成一个util方法
Statement(状态通道)和PreparedStatement(预状态通道)的区别:
创建方式不同
Statement sta=con.createStatement(); 状态通道 PreparedStatement pps=con.prepareStatemnt(sql); 预状态通道 对sql语句进行预编译并且支持占位符 在执行sql语句方法中不需要再添加sql语句了。 用于解决sql注入的问题 常用
执行sql的时机不同
状态通道是在调用executeUpdate()和executeQuery()方法时传入sql
预状态通道是在创建对象时就已经传入sql语句
预状态通道支持占位符的使用,注:赋值时,下标从1开始
项目分包
entity
实体包【存放的都是实体类】javabean
utils
工具包【存放的都是工具类】
dao(数据持久化层)
dao层【dao包】 【创将一个实体类就相应的要创建一个该实体类的dao类】
单元测试
在你的项目目录下创建一个dictory(根目录下面)
之后点击新创建的目录右键 mark Dictory 选择test那项
之后进入你要测试的类中,用idea中的快捷键 ctrl+shift+t
选择JUnit4,在勾选你要测试的方法,之后点击run
事物
con.setAutoCommit(false);//取消自动提交事务
con.commit();//提交事务
con.rollback();//回滚事务,回到数据修改前的状态
//专门为用户表服务的业务逻辑处理层 [该类中的方法发和dao中的方法是一样的] public class AccountService { private Connection con = null; AccountDao dao = new AccountDao(); public boolean account(double mon,String namein,String nameout){ try { //获取链接对象 con = JDBCUtil.getCon(); con.setAutoCommit(false);//取消自动提交 boolean booout = dao.accountJian(mon, nameout); boolean booin = dao.accountJia(mon, namein); if(booout && booin){ return true; } con.commit();//手动提交事务 } catch (Exception e) { try { con.rollback();//事务回滚 } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } return false; } }
事务的特性
原子性
原子性是指事务实施一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
一致性
事务前后数据的完整性必须保持一致
隔离性
事物的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事物所干扰,
多个并发事务之间数据要互相隔离
持久性
持久性是指一个事物一旦被提交,他对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该
对其有任何影响
PreparedStatement的批处理
当你有10条SQL语句要执行时,一次向服务器发送一条SQL语句,这么做效率上很差!处理的方案是使用批处理,即一次向服务器发送多条SQL语句,然后由服务器一次性处理。
批处理只针对更新(增、删、改)语句,批处理没有查询什么事儿!
批处理的三个方法
void】 addBatch(String sql):添加一条语句到“批”中; int[]】 executeBatch():执行“批”中所有语句。返回值表示每条语句所影响的行数据; void】 clearBatch():清空“批”中的所有语句。
代码
con = JdbcUtils.getConnection(); 连接对象 String sql = "insert into user values(?,?,?,?)"; Sql语句模板 pstt = con.prepareStatement(sql); for(int i = 0; i < 10; i++) { pstt.setString(1, "S_10" + i); 设置每一列的值 pstt.setString(2, "stu" + i); pstt.setInt(3, 20 + i); pstt.setString(4, i % 2 == 0 ? "male" : "female"); pstt.addBatch(); 添加批次 }
数据库属性文件的使用
存值方式:key-value
实现方法
在src文件夹下创建一个属性文件【jdbc.properties】
MyDriver=com.mysql.jdbc.Driver Url=jdbc:mysql://localhost:3306/C1708A Name=root Pass=1234
读取属性文件中的信息
InputStream in=当前类名.class.getClassLoader().getResourceAsStream("jdbc.properties"); 将属性文件转换成一个流 接着创建一个properties的对象,在通过这个对象来调用一个load的方法 Properties pro=new Properties(); pro.load(in);
通过key值找value值
driver=((String) pro.get("mysqldriver")).trim(); url=((String) pro.get("url")).trim(); username=((String) pro.get("username")).trim(); password=((String) pro.get("password")).trim();
在读取属性文件的过程中有一些内容只需要执行一次
static{ 加载驱动 //获取一个资源流 得到本类的加载器。在通过加载器得到一个资源流(小括号中防止的就是想要得到的资源文件) InputStream inStream = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); //创建一个属性文件的对象 Properties pro = new Properties(); try { //使用属性文件类对象调用方法来加载资源流 pro.load(inStream); driver = pro.getProperty("MyDriver"); url = (String) pro.get("Url"); name = (String) pro.get("Name"); pass = (String) pro.get("Pass"); System.out.println(JDBCUtil.class.getClass()); System.out.println(driver+" "+url+" "+name+" "+pass); //加载驱动 Class.forName(driver); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }