1. JDBC概述

在java语言中有一个专门连接数据库的规范(JDBC),用于对连接的数据库进行操作。但在这个规范内,sun公司并没有去实现,因为市面上的数据库种类太多,且实现方式都不一样,如果都由sun去实现,不现实又没有必要,所以,让数据库的产商去实现这些接口,只要想通过java去操作数据库的产商,就要自己实现该接口的方法。然后由产商提供相关的实现类包供用户下载并导入进java。

2.连接数据库的方法

本案例将使用JDBC来操作MySQL数据库

1.创建一个普通的JAVA项目,在该项目下创建一个lib目录

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

3.完成了上述操作,即可把MySQL的驱动倒入进了JAVA中,且仅限在该项目下使用,然后开始获取数据库连接对象

3.1加载注册驱动.

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

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

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

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

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

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

3.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运行进程.

操作到这一步,即可证明java可以与MySQL进行交互操作了。

3.创建表-DDL操作

/* * * 创建表操作 * 以下是在MySQL中能够正常运行的创建表SQL语句 * 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具体的实现

5.3代码

package cn.sxt.jdbc._01connection;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();}}

学习到这里,我们已经能通过java来对MySQL数据库进行一些基本的操作了,但可以发现,这样的操作繁琐且不符合开发的要求,以下将开始对代码进行简单的抽取优化。

6.JavaWeb开发的分层设计-三层框架

6.1 DAO层设计

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

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

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

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

DAO : Data Access Object 数据访问对象

层级关系:用户请求到-Web层--->Service层-->DAO层

6.2 DAO思想

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

使用DAO层概念时,我们将对我们的包命名形成一种规范,这样 规范可增强代码的阅读性及维护。

DAO层接口包命名

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

如 : cn.***.crm.dao

DAO层实现类包命名

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

如 : cn.***.crm.dao.impl

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

对应表的名称 + Dao/DAO

如 : StudentDao/DAO , TeacherDao/DAO

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

对应表的名称 + Dao/DAOImpl

如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl

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

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

如 : cn.sxt.crm.domain

对应的测试包命名

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

如 : cn.sxt.crm.test

项目的工具类包命名

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

如 : cn.sxt.crm.util/utils

整体的效果如下:

则上述代码将会优化为:

package cn.sxt.jdbc.dao.impl;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 block e.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 block e.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 block e.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.快速生成测试类

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

经过了DAO层设计之后,整个项目清晰了许多,但代码还是不够简洁,这时就需要进行代码重构了。

上述的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 配置文件,将数据库对应的账号密码写到配置文件中,然后使用程序读取配置文件内容即可

8. JdbcUtil工具类

package cn.sxt.jdbc.util;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();}}}}}

8.1小知识

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

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

9.连接池

上述的优化其实还不能满足开发需求,因为每一次访问数据库都会销毁每一次连接,耗能还会让用户等待时间过久,所以这里加入连接池的概念。

9.1连接池的思想

9.2 连接池概述

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

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

常用的DataSource的实现:

DBCP: Spring推荐的

C3P0: Hibernate推荐的

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

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

9.3准备druid连接池jar包到项目中

package cn.sxt.jdbc.test;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);}}

9.4使用连接池抽取工具类

分类: 源码分享 标签: 暂无标签

评论

暂无评论数据

暂无评论数据

目录