1. jdbc 概述

问题:实际开发中,不可能用工具或者命令行操作数据库,数据库表中的数据最终要使用Java程序来操作,那么Java中如何操作数据库中的数据呢?

答 : 在Java语言中,有一个专门连接数据库的规范(JDBC),专门负责连接数据库进行数据操作的规范

JDBC只是SUN编写的一堆接口(规范的体现),SUN公司自己并没有实现

问题 : 为什么SUN只定义一个JDBC规范,而不实现呢?

答 : 因为市面上的数据库很多,每个数据库内部接口不会向外暴露,而且即便是暴露让SUN去实现,市面上很多数据库全部要SUN来实现的话就不现实

实际中哪个数据库需要支持JAVA语言,就需要自己实现Java的JDBC规范,因为实现了JDBC很多接口,那么就会有很多实现类,而很多实现类在java中会使用一个专门的包封装起来,叫做jar包(在JDBC中叫做驱动包),各大数据库产商实现JDBC规范以后都会把他们jar包放在官网上以供开发者下载使用

1.1 JDBC

JDBC(Java DataBase Connectivity): 是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基于JDBC规范对应的API包

2. 入门案例

2.1 连接数据库

案例使用JDBC操作MySQL数据库

2.2 创建普通java项目

2.3 在项目下面新建一个lib目录

2.4 将MySQL驱动包拷贝到项目中并添加依赖

2.5 获取数据库连接对象

准备:

1.拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar

2.build path,告速项目去哪里去找字节码文件.

--------------------------------------------------------------------------------

操作JDBC的第一步,获取JDBC的连接对象.:Connection.

步骤:

1.加载注册驱动.

就是把驱动中的Driver字节码加载到JVM中.

Class.forName("com.mysql.jdbc.Driver");

为什么这句话就可以加载注册驱动?

第一步:把com.mysql.jdbc.Driver.class这份字节码加载到JVM中.

第二步:当一份字节码被加载进JVM,马上就会执行该字节码中的静态代码块.

第三步:该静态代码中,就在完成,先创建驱动对象,再注册.

2.通过DriverManager获取连接对象.

public static Connection getConnection(String url,String user,String password)

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName","root","admin");

jdbc:mysql://localhost:3306/dbName

jdbc:mysql:// :连接MySQL数据库的协议,不同数据库协议不一样

localhost:3306 :数据库软件的主机和端口

dbName 具体要连接数据库

若数据库安装在本机,并且端口是默认的3306,则可以简写:

Connection conn= DriverManager.getConnection("jdbc:mysql:///dbName","root","admin");

验证已经获取连接:可以在MySQL控制台,使用命令:show processlist; 查看MySQL运行进程。

public class GetConnectionDemo {public static void main(String[] args) throws Exception {//1.加载注册驱动 : 把当前类对应的字节码加载到JVM中Class.forName("com.mysql.jdbc.Driver");//2.获取数据库连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");System.out.println(conn);}
}

3. 创建表-DDL操作

在其他操作之间先要把数据库表要创建出来

/*创建一张t_student表:
id:
name:
age:
*//** * 创建表操作* SQL : create table t_student (id int primary key auto_increment,name varchar(50),age int)*/

   public static void main(String[] args) throws Exception {String sql = "create table t_student (id int primary key auto_increment,name varchar(50),age int)";//贾琏欲执事//1,加载注册驱动Class.forName("com.mysql.jdbc.Driver");//2,获取数据库连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");//3,创建语句对象(用于执行SQL语句的对象)Statement st = conn.createStatement();//4, 执行SQL语句//int rows = st.executeUpdate(String sql);执行DDL和DML语句,返回的是受影响的行数//ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象st.executeUpdate(sql);//5,释放资源(先开后关)st.close();conn.close();}

4. DML操作-表数据的增删改

//DML : 对表数据的增删改操作
public class DMLDemo {/** 向 t_student表中插入一条数据 * sql : insert into t_student(name,age) values ('乔峰',30)*/@Testpublic void testInsert() throws Exception {String sql = "insert into t_student(name,age) values ('乔峰',30)";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象Statement st = conn.createStatement();// 4.执行SQL语句// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象int rows = st.executeUpdate(sql);System.out.println(rows);//5.释放资源(先开后关)st.close();conn.close();}/** 删除操作: 删除t_student表中的某一条数据* SQL :delete from t_student where id = 2*/@Testpublic void testDelete() throws Exception {String sql = "delete from t_student where id = 2";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象Statement st = conn.createStatement();// 4.执行SQL语句// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象int rows = st.executeUpdate(sql);System.out.println(rows);//5.释放资源(先开后关)st.close();conn.close();}/** 修改操作 : 修改t_student表中的某一条数据* SQL : update t_student set name = '虚竹',age = 50 where id = 3*/@Testpublic void testUpdate() throws Exception {String sql = "update t_student set name = '虚竹',age = 50 where id = 3";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象Statement st = conn.createStatement();// 4.执行SQL语句// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象int rows = st.executeUpdate(sql);System.out.println(rows);//5.释放资源(先开后关)st.close();conn.close();}
}

5. DQL操作-查询操作

5.1 查询操作的分析

5.2 查询具体操作

结果集的列的位置

1. 使用 rs.next() 偏移光标,循环指定具体的某一行

获取数据的具体方法

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;import org.junit.Test;//DQL :查询操作
public class D_DQLDemo {/** 多行查询 :查询t_student表中的所有数据* SQL : select * from t_student*/@Testpublic void testList() throws Exception {String sql = "select * from t_student";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象Statement st = conn.createStatement();// 4.执行SQL语句// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象ResultSet rs = st.executeQuery(sql);//创建list集合用于封装Student对象List<Student> stus = new ArrayList<>();while(rs.next()) {//1.通过结果集的位置获取对应的数/*Object id = rs.getObject(1);Object name = rs.getObject(2);Object age = rs.getObject(3);*///2.通过结果集的 列名获取对应的数据/*Object id = rs.getObject("id");Object name = rs.getObject("name");Object age = rs.getObject("age");*///3.通过数据库数据和Java对应的数据类型获取对应的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//将获取的数据封装成对应的Student对象Student stu = new  Student(id, name, age);//将一个个Student对象添加到list集合中stus.add(stu);}for (Student student : stus) {System.out.println(student);}//5.释放资源(先开后关)rs.close();st.close();conn.close();}/** 单行查询: 查询出t_student 指定id的信息* SQL : select * from t_student where id = 1;*/@Testpublic void testGetOne() throws Exception {String sql = "select * from t_student where id = 2";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象Statement st = conn.createStatement();// 4.执行SQL语句// int rows = st.executeUpdate(String sql);执行DDL和DML语句,放回的是受影响的行数// ResultSet res = st.executeQuery(String sql);执行DQL查询语句,返回的结果集对象ResultSet rs = st.executeQuery(sql);if(rs.next()) {//1.通过结果集的位置获取对应的数/*Object id = rs.getObject(1);Object name = rs.getObject(2);Object age = rs.getObject(3);*///2.通过结果集的 列名获取对应的数据/*Object id = rs.getObject("id");Object name = rs.getObject("name");Object age = rs.getObject("age");*///3.通过数据库数据和Java对应的数据类型获取对应的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//将获取的数据封装成对应的Student对象Student stu = new  Student(id, name, age);System.out.println(stu);}//5.释放资源(先开后关)rs.close();st.close();conn.close();   }
}

6. 预编译语句对象PreparedStatment

问题 : 我们有了Statment对象可以执行SQL,为什么还要使用PreparedStatment?

优势

1. SQL语句结构清晰,参数的设置和SQL语句分离

2. 性能更高

3. 防止SQL注入

Statement: 表示静态SQL语句对象.

PreparedStatement:Statement的子接口,表示预编译SQL语句对象. 通过占位符(?)来拼SQL.

6.1 创建PreparedStatement

创建语句对象 Statment

6.2 执行SQL语句的方法

6.2.1 Statement

在执行SQL语句的时候会带上SQL语句

6.2.2 PreparedStatemeny

在执行SQL语句的方法中不需要设置SQL语句

6.3 设置站位参数的值

void setXxx(int parameterIndex,Xxx value):用于设置占位符参数,

parameterIndex:第几个问号. 注意:从1开始.

value:设置的真实值.

Xxx:表示数据类型.String/int/long/Double/Date

6.4 代码

import static org.junit.Assert.*;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;import org.junit.Test;//DML : 对表数据的增删改操作,使用预编译语句对象
public class E_DMLByPreparedStatmentDemo {/** 向 t_student表中插入一条数据 * sql : insert into t_student(name,age) values ('乔峰',30)*/@Testpublic void testInsert() throws Exception {String sql = "insert into t_student(name,age) values (?,?)";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建预编译语句对象PreparedStatement ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setString(1, "东方姑娘");ps.setInt(2, 18);// 4.执行SQL语句:注意不要带SQL参数ps.executeUpdate();//5.释放资源(先开后关)ps.close();conn.close();}/** 删除操作: 删除t_student表中的某一条数据* SQL :delete from t_student where id = 2*/@Testpublic void testDelete() throws Exception {String sql = "delete from t_student where id = ?";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建预编译语句对象PreparedStatement ps = conn.prepareStatement(sql);//3.1设置占位符对应的参数值ps.setInt(1, 1);// 4.执行SQL语句int rows = ps.executeUpdate();System.out.println(rows);//5.释放资源(先开后关)ps.close();conn.close();}/** 修改操作 : 修改t_student表中的某一条数据* SQL : update t_student set name = '虚竹',age = 50 where id = 3*/@Testpublic void testUpdate() throws Exception {String sql = "update t_student set name = ?,age = ? where id = ?";// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建预编译语句对象PreparedStatement ps = conn.prepareStatement(sql);//3.1设置占位符参数对应的值ps.setString(1, "西方失败");ps.setInt(2, 40);ps.setInt(3, 4);// 4.执行SQL语句int rows = ps.executeUpdate();System.out.println(rows);//5.释放资源(先开后关)ps.close();conn.close();}
}

7. JavaWeb开发的分层设计-三层架构

实际开发中,JavaWeb开发代码一般分为三层,分层结构是JavaWeb开发中的一种设计思想,这样会让我们开发层次分明,每一层只要完成对应的功能即可,使得项目便于开发和维护

1 . Web层/表现层 : 主要接受前台浏览器用户的参数,给浏览器响应数据等等

2. Service层/业务成/服务层:主要处理业务功能,日志,权限,事物,等等

3. DAO层/持久层 :专门负责和数据库交互,数据处理相关代码

DAO : Data Access Object 数据访问对象

实际开发中 : 用户请求到-Web层--->Service层-->DAO层

7.2 DAO思想

7.3 使用DAO以后代码的以及包的设计结构

开发中如果使用的分层,编写的包和类名接口名等等都是有固定规则,不能随便瞎写

7.3.1 DAO层接口包命名

公司域名倒写+项目名称/模块名称+dao

如 : cn.sxt.crm.dao

7.3.2 DAO层实现类包命名

公司域名倒写+项目名称/模块名称+dao+impl

如 : cn.sxt.crm.dao.impl

7.3.3 DAO层操作对应表的接口命名

对应表的名称 + Dao/DAO

如 : StudentDao/DAO , TeacherDao/DAO

7.3.4 DAO层操作对应表的实现类命名

对应表的名称 + Dao/DAOImpl

如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl

7.3.5 数据表对应的Java类domain/pojo包命名

POJO(Plain Ordinary Java Object)简单的Java对象
domian : 域对象

公司域名倒写+项目名称/模块名称+domain/pojo

如 : cn.sxt.crm.domain

7.3.6 对应的测试包命名

公司域名倒写+项目名称/模块名称+test

如 : cn.sxt.crm.test

7.3.7 项目的工具类包命名

公司域名倒写+项目名称/模块名称+util/utils

如 : cn.sxt.crm.util/utils

7.3.8 DAO代码设计结构

7.3.9 Dao的实现类代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;import cn.sxt.jdbc.dao.StudentDao;
import cn.sxt.jdbc.domain.Student;public class StudentDaoImpl implements StudentDao {@Overridepublic int saveStudent(Student stu) {String sql = "insert into t_student(name,age) values (?,?)";Connection conn = null;PreparedStatement ps = null;try {// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建预编译语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());// 4.执行SQL语句:注意不要带SQL参数return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {//5.释放资源(先开后关)try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return 0;}@Overridepublic int deleteById(int id) {String sql = "delete from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;try {// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建预编译语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setInt(1, id);// 4.执行SQL语句:注意不要带SQL参数return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {//5.释放资源(先开后关)try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return 0;}@Overridepublic int updateStudentById(Student stu) {String sql = "update t_student set name = ?,age = ? where id = ?";Connection conn = null;PreparedStatement ps = null;try {// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建预编译语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());ps.setInt(3, stu.getId());// 4.执行SQL语句:注意不要带SQL参数return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {//5.释放资源(先开后关)try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return 0;}@Overridepublic Student selectById(int id) {String sql = "select * from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数对应的值ps.setInt(1, id);// 4.执行SQL语句rs = ps.executeQuery();if(rs.next()) {//通过数据库数据和Java对应的数据类型获取对应的只String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//将获取的数据封装成对应的Student对象Student stu = new  Student(id, name, age);return stu;}} catch (Exception e) {// TODO: handle exception}finally {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}return null;}@Overridepublic List<Student> selectList() {String sql = "select * from t_student";//创建list集合用于封装Student对象List<Student> stus = new ArrayList<>();Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {// 1.加载注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取数据库连接对象conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root");// 3.创建语句对象ps = conn.prepareStatement(sql);// 4.执行SQL语句rs = ps.executeQuery();while(rs.next()) {//通过数据库数据和Java对应的数据类型获取对应的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//将获取的数据封装成对应的Student对象Student stu = new  Student(id, name, age);//将一个个Student对象添加到list集合中stus.add(stu);}} catch (Exception e) {// TODO: handle exception}finally {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}return stus;}}

7.3.10 快速生成单元测试类

一个dao层或者service编写代码以后,需要为每一个功能都进行单元测试,一个dao中的方法很多。我们快速为这个dao层的类生成单元测试类,(dao的每一个方法都自动生成一个测试方法)

7.4 代码初步重构

上述的DAO方法中的代码,存在的问题:

问题1:每个DAO方法中都会写:驱动名称/url/账号/密码,不利于维护.

解决方案: 声明为成员变量即可.(在被类中任何地方都可以访问)

问题2:问题1的解决方案有问题.

每个DAO实现类里都有一模一样的4行代码,不利于维护(考虑有100个DAO实现类,就得重复99次).

解决方案: 把驱动名称/url/账号/密码这四行代码,专门抽取到一个JDBC的工具类中.---->JdbcUtil.

问题3:其实DAO方法,每次操作都只想需要Connection对象即可,而不关心是如何创建的.

解决方案:把创建Connection的代码,抽取到JdbcUtil中,并提供方法getConn用于向调用者返回Connection对象即可.

问题4:每次调用者调用getConn方法的时候,都会创建一个Connection对象.

但是,每次都会加载注册驱动一次.--->没必要的.

解决方案:把加载注册驱动的代码放在静态代码块中--->只会在所在类被加载进JVM的时候,执行一次.

问题5:每个DAO方法都要关闭资源.(鸡肋代码).

解决方案:把关闭资源的代码,抽取到JdbcUtil中.

public static void close(Connection conn, Statement st, ResultSet rs) {}

调用者:

DML: JdbcUtil.close(conn,st,null);

DQL: JdbcUtil.close(conn,st,rs);

问题6 :连接数据库的账号密码写死在JdbcUtil工具类中了,不利于维护

抽取 db.properties 配置文件,将数据库对应的账号密码写到配置文件中,然后使用程序读取配置文件内容即可

7.4.1 jdbcUtil 工具类

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;public class JdbcUtil {// alt+shif+a 多行修改,修改以后还原 alt+shif+a/*private static String driverClassName = "com.mysql.jdbc.Driver";private static String url = "jdbc:mysql://localhost:3306/jdbcdemo";private static String username = "root";private static String password = "root";*/private static Properties p = new Properties();static {try {//1.获取类加载器ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//2,使用类加载器获取项目 类路径下面的文件InputStream inputStream = classLoader.getResourceAsStream("db.properties");//3.使用Priperties加载配置文件对应的输入流p.load(inputStream);Class.forName(p.getProperty("driverClassName"));} catch (Exception e) {e.printStackTrace();}}public static Connection getConnection() {try {return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));} catch (Exception e) {e.printStackTrace();throw new RuntimeException("亲,连接数据库失败", e);}}public static void close(Connection conn,PreparedStatement ps,ResultSet rs) {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(ps !=null) {ps.close();}} catch (SQLException e) {e.printStackTrace();}finally {try {if(conn !=null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}}}
}

7.4.2 使用工具类以后的DAO实现类效果

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;import cn.sxt.jdbc.dao.StudentDao;
import cn.sxt.jdbc.domain.Student;
import cn.sxt.jdbc.util.JdbcUtil;public class StudentDaoImpl implements StudentDao {@Overridepublic int saveStudent(Student stu) {String sql = "insert into t_student(name,age) values (?,?)";Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();// 3.创建预编译语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());// 4.执行SQL语句:注意不要带SQL参数return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, null);}return 0;}@Overridepublic int deleteById(int id) {String sql = "delete from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();// 3.创建预编译语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setInt(1, id);// 4.执行SQL语句:注意不要带SQL参数return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, null);}return 0;}@Overridepublic int updateStudentById(Student stu) {String sql = "update t_student set name = ?,age = ? where id = ?";Connection conn = null;PreparedStatement ps = null;try {conn = JdbcUtil.getConnection();// 3.创建预编译语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数ps.setString(1, stu.getName());ps.setInt(2, stu.getAge());ps.setInt(3, stu.getId());// 4.执行SQL语句:注意不要带SQL参数return ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, null);}return 0;}@Overridepublic Student selectById(int id) {String sql = "select * from t_student where id = ?";Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JdbcUtil.getConnection();// 3.创建语句对象ps = conn.prepareStatement(sql);//3.1设置占位符参数对应的值ps.setInt(1, id);// 4.执行SQL语句rs = ps.executeQuery();if(rs.next()) {//通过数据库数据和Java对应的数据类型获取对应的只String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//将获取的数据封装成对应的Student对象Student stu = new  Student(id, name, age);return stu;}} catch (Exception e) {// TODO: handle exception}finally {try {if(rs !=null) {rs.close();}} catch (SQLException e) {e.printStackTrace();}finally {JdbcUtil.close(conn, ps, rs);}}return null;}@Overridepublic List<Student> selectList() {String sql = "select * from t_student";//创建list集合用于封装Student对象List<Student> stus = new ArrayList<>();Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JdbcUtil.getConnection();// 3.创建语句对象ps = conn.prepareStatement(sql);// 4.执行SQL语句rs = ps.executeQuery();while(rs.next()) {//通过数据库数据和Java对应的数据类型获取对应的只int id = rs.getInt("id");String name = rs.getString("name");int age = rs.getInt("age");//System.out.println(id+","+name+","+age);//将获取的数据封装成对应的Student对象Student stu = new  Student(id, name, age);//将一个个Student对象添加到list集合中stus.add(stu);}} catch (Exception e) {// TODO: handle exception}finally {JdbcUtil.close(conn, ps, rs);}return stus;}}

7.5 知识点补充,类加载器

在项目的 类路径(src)下面创建一个 db.properties配置文件,专门配置连接数据库的账号密码

如何使用类加载器加载配置文件

7.5.1 配置文件

7.5.1.1 配置文件创建的位置

配置文件一般都放在项目的src 源目录下面

7.5.2 加载代码

import static org.junit.Assert.*;
import java.io.InputStream;
import java.util.Properties;
import org.junit.Test;public class PropertiesTest {@Testpublic void testName() throws Exception {/** ClassLoader 类加载器* ClassLoader :可以从项目的类路径下面读取对应的配置文件返回一个输入流* ClassLoader 在程序运行的时候JVM已经为每一个项目都创建了一个,我们开发者只需要获取即可* 获取类加载器方式* 1、使用当前线程* ClassLoader classLoader = Thread.currentThread().getContextClassLoader();* 2、通过某一类的字节码实例也可以获取* ClassLoader classLoader = PropertiesTest.class.getClassLoader();*/ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//使用类加载器获取项目 类路径下面的文件InputStream inputStream = classLoader.getResourceAsStream("db.properties");/** Properties 是Map集合下面的一个 专门用于读取配置文件的对象* 可以读取当前类路径下面的  xxx.properites类型的配置文件* * xxx.properites的内容必须是key=value 键值对的数据*///1.创建Properties对象Properties p = new Properties();//2.加载配置文件p.load(inputStream);System.out.println(p);//获取具体某一个key对应的值String driverClassName = p.getProperty("driverClassName");System.out.println(driverClassName);}
}

7.5.3 效果

8. 连接池

8.1 遇到的问题-引出连接池

8.2 连接池思想

8.3 连接池的概述

在Java中,连接池使用javax.sql.DataSource接口来表示连接池.

注意:DataSource仅仅只是一个接口,由各大服务器厂商来实现(Tomcat.JBoss,阿里巴巴).

常用的DataSource的实现:

DBCP: Spring推荐的

C3P0: Hibernate推荐的

Druid : (德鲁伊)阿里巴巴开源的,性能最好,速度最快

DataSource(数据源)和连接池(Connection Pool)是同一个。

8.4 使用连接池和不使用连接池的区别在哪里

从代码上:

不使用连接池: Conenction对象由DriverManager获取.

Connection conn = DriverManager.getConnection(url,username,password);

使用连接池:

如何创建DataSource对象,如何在DataSource中设置url,账号,密码.

Connection conn = DataSource对象.getConnection();

--------------------------------------------------------------------

使用连接池的时候:

释放资源: Connection对象.close():

是把Connection放回给连接池,而不是和数据库断开.

8.5 Druid连接池的使用

8.5.1 准备druid 连接池jar包到项目

import static org.junit.Assert.*;import java.io.InputStream;
import java.io.Reader;
import java.sql.Connection;
import java.util.Properties;import javax.sql.DataSource;import org.junit.Test;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;public class DataSourceTest {// 直接创建连接池对象@Testpublic void testName() throws Exception {// 1.创建连接池对象DruidDataSource ds = new DruidDataSource();// 2.设置连接数据库的账号密码ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo");ds.setUsername("root");ds.setPassword("root");ds.setMaxActive(10);// 最大连接数// 3.获取连接对象Connection conn = ds.getConnection();System.out.println(conn);}// 使用工厂对象创建连接池对象,工厂对象的好处,不需要直接设置账号密码等等,只需要将// 连接数据库的账号密码等等以指定的 key的名称配置到 xxx.properties文件中即可,工厂对象底层自动读取@Testpublic void testDataSourceByFactory() throws Exception {// 1.获取类加载器用于加载clsspath下面的 配置文件ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 2.读取druid.properties配置文件InputStream inputStream = classLoader.getResourceAsStream("druid.properties");// 3.创建Properties对象,并读取配置文件对应的输入流Properties p = new Properties();p.load(inputStream);// 4.创建连接池对象DataSource ds = DruidDataSourceFactory.createDataSource(p);// 5.获取连接对象Connection conn = ds.getConnection();System.out.println(conn);}
}

8.5.2 db.propperties

8.5.3 使用Druid抽取的工具类

9. 事务

案例:银行转账:从张无忌账户上给赵敏转1000块.

准备:account(账户表):

---------------------------------------------------------------

id name(账号,唯一) balance(余额)

1 张无忌 20000

2 赵敏 0

---------------------------------------------------------------

转账的思路:

1.检查张无忌的账号余额是否大于等于1000.

SQL: SELECT balance FROM account WHERE name = '张无忌' AND balance >=1000

余额>=1000:GOTO 2:

余额 <1000:提示:亲,你的余额不足.

2.在张无忌的账号余额上减少1000.

SQL: UPDATE account SET balance = balance-1000 WHERE name = '张无忌'

3.在赵敏的账户余额尚增加1000.

SQL: UPDATE account SET balance = balance+1000 WHERE name = '赵敏'

-------------------------------------------------------------------------------------------

注意:在第二步和第三步之间,停电了.

使用异常模拟停电:System.out.println(1/0);

9.1 事务概述

事务(Transaction,简写为tx):

在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。

为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:

当每个逻辑操作单元全部完成时,数据的一致性可以保持,

而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。

--------------------------------------------------

事务的ACID属性:

1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)

3. 隔离性(Isolation)

事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

4. 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

--------------------------------------------------

事务:指构成单个逻辑工作单元的操作集合

事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态

处理事务的两个动作:

提交:commit: 当整个事务中,所有的逻辑单元都正常执行成功. ---->提交事务.---数据已经提交,不能更改.

回滚:rollback: 当整个事务中,有一个逻辑单元执行失败, ---->回滚事务.

撤销该事务中的所有操作--->恢复到最初的状态.

---------------------------------------------------------------------------------------------------

如何在代码中去处理事务:

1.在JDBC中,事务是默认自动提交的. 必须先设置事务为手动提交.

connection对象.setAutoCommit(false);//设置事务为手动提交.

2.手动的提交事务.

connection对象.commit();

3.若出现异常必须回滚事务:

不回滚事务,总余额依然是正确的. 若不回滚事务,不会释放数据库资源.

connection对象.rollback();

-----------------------------------------------------------------------------------

1.在JDBC在事务是默认提交的,那是在什么时候提交的.

在执行一个DML/DDL操作的时候,就已经提交事务了.

2.针对于CRUD操作. 只有DML操作才有事务,查询操作没有事务.

但是,我们一般会把查询也放在事务里面.

4. 以后,凡是发现自己编写的代码是正确的,测试也通过,但是就是数据库表中的数据不变----->事务没提交的问题.

4.MySQL中,InnoDB支持外键.支持事务,MyISAM不支持外键,不支持事务.

InnoDB存储引擎: 支持事务,支持外键,但是查询效率略低,(金融,理财,p2p)

MyISAM存储引擎:不支持事务和外键,但是查询效率较高(新闻网站)

Oracle 不存在存储引擎,都有事务

9.2 事务处理代码

public class TransactionTest {@Testpublic void testName() throws Exception {Connection conn = null;Statement st = null;ResultSet rs = null;try {conn = DruidUtil.getConnection();//将事务设置为手动提交conn.setAutoCommit(false);st = conn.createStatement();// 1.检查张无忌的账号余额是否大于等于1000.rs = st.executeQuery("SELECT balance FROM account WHERE name = '张无忌' AND balance >=1000");if(!rs.next()) {throw new RuntimeException("亲,您的账户余额不够");}// 余额>=1000:GOTO 2:// 余额 <1000:提示:亲,你的余额不足.// 2.在张无忌的账号余额上减少1000.st.executeUpdate("UPDATE account SET balance = balance-1000 WHERE name = '张无忌'");System.out.println(1/0);// 3.在赵敏的账户余额尚增加1000.st.executeUpdate("UPDATE account SET balance = balance+1000 WHERE name = '赵敏'");//提交事务conn.commit();} catch (Exception e) {e.printStackTrace();//回滚事务conn.rollback();}finally {DruidUtil.close(conn, st, rs);}}
}

jdbc连接数据scanip_java数据库连接_jdbc相关推荐

  1. java开发中jdbc连接数据 库的操作代码

    2019独角兽企业重金招聘Python工程师标准>>> JDBC连接数据库 •创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要 ...

  2. jdbc连接mysql驱动包_jdbc连接数据库驱动包

    FineReport如何用JDBC连接阿里云ADS数据库 在使用FineReport连接阿里云的ADS(AnalyticDB)数据库,很多时候在测试连接时就失败了.此时,该如何连接ADS数据库呢? 我 ...

  3. jdbc连接mysql工具类_jdbc之工具类DBUtil的使用

    首先回顾一下jdbc的使用方法: 1. 注册驱动 2. 建立连接 3. 建立statement 4. 定义sql语句 5. 执行sql语句,如果执行的是查询需遍历结果集 6. 关闭连接 其中建立连接和 ...

  4. jdbc连接mysql的语法_JDBC连接MySQL

    JDBC连接MySQL 加载及注册JDBC驱动程序 Class.forName("com.mysql.jdbc.Driver"); Class.forName("com. ...

  5. jdbc连接mysql数据库过程_jdbc连接数据库的步骤

    基本理解:开发中要先加载驱动,然后获得连接,创建执行SQL语句的对象,并且执行SQL 第一步:把想要连接的数据库驱动加载入JVM,如加载mysql数据库驱动类可以通过Class.forName(&qu ...

  6. jdbc连接池工作原理_JDBC连接实际上如何工作?

    jdbc连接池工作原理 The JDBC Connection interface is a part of java.sql package. The java.sql.Connection int ...

  7. jdbc连接数据scanip_JDBC连接数据库的四种方式:DriverManager,DataSource,DBCP,C3P0

    方法1:使用java.sql.DriverManager类 驱动管理器类,用于管理所有注册的驱动程序. (注:DataSource 接口是 JDBC 2.0 API 中的新增内容,它提供了连接到数据源 ...

  8. jdbc连接mysql正规方法_JDBC基础篇(MYSQL)——通过JDBC连接数据库的三种方式

    package day01_jdbc; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManage ...

  9. jdbc连接mysql驱动方式_JDBC连接数据库系列教程 – JDBC注册驱动的常见方式

    方法1 DriverManager.registerDriver(new com.microsoft.sqlserver.jdbc.SQLServerDriver(),2); 方法2 System.s ...

最新文章

  1. 黑马程序员pink老师前端入门教程,零基础必看的JavaScript基础语法视频教程
  2. 二级级联菜单---纯js实现
  3. python装饰器副作用_对Python 装饰器的理解心得
  4. TIMESTAMP和DATETIME哪个好
  5. Eclipse 使用 CodeMix 插件 开发 Vue.js
  6. 韩媒体:900万人个人信息被非法泄露到中国
  7. CVPR 2019 | Stereo R-CNN 3D 目标检测
  8. html图片怎么弄透明背景,如何使用CSS实现背景图像透明
  9. 【附源码】Python计算机毕业设计汽车4S店管理系统
  10. 589页22万字城市智慧应急指挥中心大数据信息化系统整体设计方案
  11. n维椭球体积公式_【栗子资料】高中生必看,高中年级所有数学公式大全
  12. JVM运行原理及优化
  13. Python数据分析基础(2)
  14. Error: Cannot find module 'util-deprecate'
  15. 如何让随机出现的列表元素只出现一次 python——求解
  16. R语言 多个变量进行 卡方检验 循环 fisher检验 chi test 循环
  17. ios 中字符串怎么换行
  18. 【MOOS系统安装步骤】
  19. 利用Nginxcp为cPanel/WHM服务器开启nginx支持
  20. 让自己分享的网址在QQ中更有逼格

热门文章

  1. 《第3选择》学习笔记
  2. android错误-android.util.AndroidRuntimeException:You cannot combine custom titles with other title
  3. 2.2 《数据库系统概论》之关系操作、关系完整性、关系代数
  4. ppt批量缩略图_PPT如何在文件夹下显示缩略图的方法
  5. 32位 shell.efi x86_Linux 的 32 位内核自 Meltdown 以来一直有问题
  6. php array_only,php可以定义数组的常量吗
  7. Cpp 对象模型探索 / 父类和子类调用构造函数和析构函数的顺序
  8. oracle正确维护归档,转载:ORACLE正确删除归档日志的方法
  9. excel几个表合成一张_快速将多个excel表合并成一个excel表
  10. linux内核定时唤醒,Linux内核时钟与定时器的实现