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使用连接池抽取工具类
暂无评论数据