java学习第九周总结

这一周主要学习了JDBC和HTML的开头。学习JDBC时从基础的核心六步开始,一步步扩展,结合多个案例去学习知识点以及书写步骤,在案例中寻找问题,然后引入新的知识点去优化,去除冗余代码,提高灵活性、复用性。
知识点总结如下:

JDBC

一、引言


1.1 如何操作数据

使用客户端工具访问数据库,需要手工建立链接,输入用户名和密码登录,编写SQL语句,点击执行,查看操作结果(结果集或受影响行数)。

1.2 实际开发中,会采用客户端操作数据库吗?

在实际开发过程中,当用户的数据发生改变时,不可能通过客户端操作执行SQL语句,因为操作量过大!无法保证效率和正确性

二、JDBC(Java DataBase Connectivity)


2.1 什么是JDBC?

JDBC(Java DataBase Connectivity) Java连接数据库,可以使用Java语言连接数据库完成CRUD操作

2.2 JDBC核心思想

Java中定义了访问数据库的接口,可以为多种关系型数据库提供统一的访问方式。

由数据库厂商提供驱动实现类(Driver数据库驱动)

2.2.1 MySQL数据库驱动

  • mysql-connector-java-5.1.X 适用于5.X版本
  • mysql-connector-java-8.0.X 适用于8.X版本

2.2.2 JDBC API

JDBC 是由多个接口和类进行功能实现

类型 全限定名 简介
class java.sql.DriverManager 管理多个数据库驱动类,提供了获取数据库连接的方法
interface java.sql.Connection 代表一个数据库连接(当Connection不是NULL时,表示已连接一个数据库)
interface java.sql.Statement 发送SQL语句到数据库的工具
interface java.sql.ResultSet 保存SQL查询语句的结果数据(结果集)
class java.sql.SQLException 处理数据库应用程序时所发生的异常

2.3 环境搭建

  1. 在项目下新建 lib 文件夹,用于存放 jar 文件
  2. 将MySQL驱动文件mysql-connector-java-5.1.25-bin.jar 复制到项目的lib文件夹中
  3. 选中lib文件夹 右键选择 add as library,点击OK

三、JDBC开发步骤【重点】


3.1 注册驱动

使用Class.forName(“com.mysql.jdbc.Driver”); 手动加载字节码文件到JVM中

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

3.2 连接数据库

  • 通过DriverManager.getConnection(url,user,password);获得数据库连接对象

    • URL:jdbc:mysql://localhost:3306/database

    • user:root

    • password:1234

      Connection connection = DriverManager.getConnection(“jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf8”,“root”,“1234”);

  • URL(Uniform Resource Locator)统一资源定位符:由协议、IP、端口、SID(程序实例名称)组成

3.3 获取发送SQL的对象

通过Connection对象获得Statement对象,用于对数据库进行通用访问的

Statement statement = connection.createStatement();

3.4 执行SQL语句

编写SQL语句,并执行,接收执行后的结果

int result = statement.executeUpdate("update stu set student_name='高强',sex='女' where student_id = 'S1003'");
  • 注意:在编写DML语句时,一定要注意字符串参数的符号是单引号 ‘值’
  • DML语句:增、删、改时,执行的结果是受影响行数(int类型)。
  • DQL语句:查询时,返回的是数据结果集(ResultSet结果集)

3.5 处理结果

接收并处理操作结果

if(result > 0){
	System.out.println("执行成功");
}
  • 受影响行数:逻辑判断,方法返回
  • 查询结果集:迭代、依次获取

3.6 释放资源

遵循的是先开后关的原则,释放过程中用到的所有资源对象

statement.close();
connection.close();

3.7 综合案例

综合核心六步,实现增删改

 public static void main(String[] args)throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2.连接数据库
        String url = "jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8";
        String user = "root";
        String password="1234";
        Connection connection=DriverManager.getConnection(url,user,password);

        //3.获取发送SQL的对象
        Statement statement = connection.createStatement();

        //4.编写SQL语句,执行SQL语句(返回受影响行数)
        //新增
//        String sql = "insert into stu(student_id,student_name,sex,birthday,phone,GradeId) values ('S1005','高强','女','1999-09-09','15612341234',3)";
//        String sql = "delete from stu where student_id = 'S1005'";删除
        //修改
        String sql = "update stu set student_name='高强',sex='女' where student_id = 'S1003'";
        int result = statement.executeUpdate(sql);
        //5.处理数据
        if(result > 0){
            System.out.println("执行成功!");
        }
        //6.释放资源 先开后关
        statement.close();
        connection.close();

    }

四、 ResultSet(结果集)


在执行查询SQL后,存放查询到的结果集数据

4.1 接收结果集

ResultSet rs = statement.executeQuery(sql)

ResultSet rs = statement.executeQuery("SELECT * FROM stu");

4.2 遍历ResultSet中的数据

ResultSet以表(Table)结构进行临时结果的存储,需要通过JDBC API将其中的数据进行依次获取

  • 数据行指针:初始位置在第一行数据前,每调用一次boolean next()方法,ResultSet中指针向下移动一行,结果为true,表示当前行有数据

  • rs.getXxx(“列名”); 根据列名获得数据

  • rs.getXxx(整数下标); 代表根据列的编号顺序获得!从1开始

    boolean next() throws SQLException;//判断rs结果集中下一行是否有数据

4.2.1 遍历方法

int getInt(int columnIndex) throws SQLException;//获得当前行的第N列的int值
int getInt(String columnLabel) throws SQLException;//获得当前行columnLabel列的int值
  • 注意:列的编号从1开始

4.3 综合案例

对stu表所有的数据进行遍历

4.3.1 根据列的名称获取

package com.qf.day42.t2.basic;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class TestDql {
    public static void main(String[] args)  throws Exception{
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //2.获得连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8","root","1234");

        //3.获取执行SQL的对象
        Statement statement = connection.createStatement();

        //4.编写SQL语句
        String sql = "select student_id,student_name,sex,birthday,phone,GradeId from stu;";
        ResultSet resultSet = statement.executeQuery(sql);

        //5.处理结果 (结果集!)
        while(resultSet.next()){//判断结果集中是否有下一行!
            //根据列名获取当前行每一列的数据
            String student_id = resultSet.getString("student_id");
            String student_name = resultSet.getString("student_name");
            String sex = resultSet.getString("sex");
            String birthday = resultSet.getString("birthday");
            String phone = resultSet.getString("phone");
            int gradeId = resultSet.getInt("gradeId");
            System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
        }

        //6.释放资源
        resultSet.close();
        statement.close();
        connection.close();

    }
}

4.3.2 根据列的下标获取

 //5.处理结果 (结果集!)
        while(resultSet.next()){//判断结果集中是否有下一行!
            //根据列的编号获取当前行每一列的数据
            String student_id = resultSet.getString(1);
            String student_name = resultSet.getString(2);
            String sex = resultSet.getString(3);
            String birthday = resultSet.getString(4);
            String phone=  resultSet.getString(5);
            int gradeId  = resultSet.getInt(6);
            System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
 }

五、常见错误

  • java.lang.ClassNotFoundException 找不到类(类名书写错误、没有导入jar包)
  • com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException 与SQL语句相关的错误(表名列名书写错误、约束错误、插入的值是String类型,但是没有加单引号)建议:在客户端工具中测试sql语句后,再粘贴到代码中来
  • com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry ‘S1003’ for key ‘PRIMARY’ 原因:主键值已存在!更改要插入的主键值
  • com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Unknown column ‘password’ in
    • 可能输入的值的类型不对,确定插入元素时,对应的值的类型是否争取

六、综合案例【登录】


6.1 创建表

  • 创建一张用户表 User
    • id 主键、自动增长
    • username 字符串类型 非空
    • password 字符串类型 非空
    • phone 字符串类型
  • 插入2条测试语句

6.2 实现登录

  • 通过控制台,用户输入用户名和密码
  • 用户输入的用户名和密码作为参数,编写查询SQL语句。
  • 如果查询到用户,则用户存在,提示登录成功,反之,提示失败!

七、SQL注入问题


7.1 什么是SQL注入

当用户输入的数据中有SQL关键字或语法时,并且参与了SQL语句的编译,导致SQL语句编译后条件结果为true,一直得到正确的结果。称为SQL注入

7.2如何避免SQL注入

由于编写的SQL语句,是在用户输入数据后,整合后再编译成SQL语句。所以为了避免SQL注入的问题,使SQL语句在用户输入数据前,SQL语句已经完成编译,成为了完整的SQL语句,再进行填充数据

八、 PreparedStatement【重点】


PreparedStatement接口继承了Statement接口。执行SQL语句的方法没有区别!

8.1 PreparedStatement的应用

作用:1.预编译SQL语句,效率高!

			2.安全,避免SQL注入

			3.可以动态的填充数据,执行多个同构的SQL语句

8.1.1 参数标记

//1.预编译SQL语句
PreparedStatement pstmt = connection.prepareStatement(sql);
  • 注意:PreparedStatement应用时,SQL字符串的参数都由?符号站位,被称为参数标记。在执行该SQL语句前,要为每个?参数赋值

8.1.2 动态参数绑定

pstmt.setXxx(下标,值); 参数下标是从1开始,为指定占位符下标绑定值

//2.为占位符下标赋值
pstmt.setString(1,username);
pstmt.setString(2,password);

九、综合练习


9.1创建数据库、表

数据库 Account

  • 创建一张表 t_ccount。有以下列
    • cardId:字符串,主键
    • password:字符串,非空
    • username:字符串,非空
    • balance:小数,非空
    • phone:字符串,非空

9.2 创建项目通过JDBC实现功能

创建AccountSystem类,完成下列功能

  • 开户:控制台输入所有的账户信息,使用PreparedStatement添加至t_account表
  • 存款:控制台输入卡号、密码、存储金额进行修改
  • 取款:输入卡号、密码、取款金额
  • 转账:输入卡号、密码、对方卡号、转账金额进行修改
  • 修改密码:控制台输入卡号、密码,再输入新密码进行修改
  • 注销:控制台输入卡号、密码,删除对应的账户信息

十、封装工具类


10.1 重用性方案

  • 封装了获取连接、释放资源两个方法
    • 提供public static Connection getConnection()方法
    • 提供public static void closeAll(Connection conn,Statement statement,ResultSet resultSet)

10.1.1 工具实现

package com.qf.day43.t1;

import sun.plugin2.gluegen.runtime.CPU;

import java.sql.*;

/**
 * 数据库工具类
 * 1、提供连接 -->Connection
 * 2、提供统一资源关闭
 * 可重用性方案
 */
public class DBUtils {


    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //硬编码
    //获得连接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=UTF8", "root", "1234");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    //释放资源
    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

10.2 跨平台方案

  • 定义 private static final Properties properties = new Properties();//配置文件集合
  • 定义static{
    //首次使用工具类,触发类加载
    InputStream is = DBUtils.class.getResourceAsStream(“路径”);//复用本类自带流,读取配置文件
    properties.load(is);//将is流中的配置文件信息,加载到集合中
    Class.forName(properties.getProperty(“driver”));
    }
  • 在getConnection方法中。应用properties.getProperty(“url”);

10.2.1 实现

在src目录下新建db.properties文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8
username=root
password=123456

DBUtils代码实现

package com.qf.day43.t2;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * 数据库工具类
 * 1、获取连接 connection
 * 2、释放资源
 * 可跨平台方案
 */
public class DBUtils {
   private static final Properties properties = new Properties();
    static {
        try {
                //适用类自身自带的流                                        路径
                InputStream is = DBUtils.class.getResourceAsStream("/db.properties");

                properties.load(is);//通过流,将配置信息的内容分割成键值对

                Class.forName(properties.getProperty("driver"));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
        }
    }

    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(properties.getProperty("url"),properties.getProperty("username"),properties.getProperty("password"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }


    //释放资源
    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

十一、ORM


ORM(Object Relational Mapping)

从数据库查询到的结果集(ResultSet)在进行遍历时,逐行遍历,取出的都是零散的数据。在实际应用开发中,我们需要将零散的数据进行封装整理

11.1 ORM 实体类(entity):零散数据的载体

  • 一行数据中,多个零散的数据进行整理
  • 通过entity的规则对表中的数据进行对象的封装
  • 表名=类名;列名=属性名;提供各个属性的get、set方法
  • 提供无参构造方法、(视情况添加有参构造)

11.1.1 ORM应用

package com.qf.day43.t2;

public class User {
    private int id;
    private String username;
    private String password;
    private String sex;
    private String email;
    private String address;
    public User(){}

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public User(int id, String username, String password, String sex, String email, String address) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.sex = sex;
        this.email = email;
        this.address = address;
    }
}




package com.qf.day43.t2;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class OrmSelect {
    public static void main(String[] args) {
        Connection connection = DBUtils.getConnection();
        String sql = "select id,username,PASSWORD,sex,email,address from `user`;";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            preparedStatement = connection.prepareStatement(sql);
            System.out.println(preparedStatement);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {//拿到每一行数据、
                //拿到每一列的数据
                User user = new User();
                int id = resultSet.getInt("id");
                String username = resultSet.getString("username");
                String password = resultSet.getString("password");
                String sex = resultSet.getString("sex");
                String email = resultSet.getString("email");
                String address = resultSet.getString("address");
                //将一行中零散的数据,封装在一个User对象里。
                user.setId(id);
                user.setUsername(username);
                user.setPassword(password);
                user.setSex(sex);
                user.setEmail(email);
                user.setAddress(address);
                System.out.println(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(connection, preparedStatement, resultSet);
        }
    }
}

十二、DAO(Data Access Object)


数据访问对象

  • 将所有对同一张表的操作都封装在一个XXXDaoImpl对象中。
  • 根据增删改查的不同功能,实现具体的方法(insert,update,delete,select,selectAll)
  • 经验:对于任何一张表中的数据进行操作时,无非就是增、删、改、查。应将对于一张表的所有操作统一封装在一个数据访问对象中。重用

十三、日期类型


  • java.util.Date
    • Java语言常规应用层面的日期类型。可以通过字符串创建对应的时间对象
    • 无法直接通过JDBC插入数据库
  • java.sql.Date
    • 不可以通过字符串创建对应的时间对象。只能通过毫秒值创建对象(1970年1月1日至今的毫秒值)
    • 可以直接通过JDBC插入数据库

13.1 日期格式化工具

SimpleDateFormat 日期格式化

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//按照指定格式转换成util.Date类型
java.util.Date date = sdf.parse("2000-01-01");

13.2 日期工具类 DateUtil

package com.qf.day43.t4;

import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * 日期转换
 * 字符串转UtilDate
 * 字符串转SqlDate
 * utilDate转成Sqldate
 */
public class DateUtils {
    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    //字符串转Util
    public static java.util.Date strToUtilDate(String str) {
        try {
            return simpleDateFormat.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
    //字符串转sql
//    public static java.sql.Date strToSqlDate(String str){
//        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
//        try {
//            java.util.Date date = simpleDateFormat.parse(str);
//            return new java.sql.Date(date.getTime());
//        } catch (ParseException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }

    //util转sql
    public static java.sql.Date utilToSql(java.util.Date date){
        return new java.sql.Date(date.getTime());
    }

}

13.2.1 测试

package com.qf.day43.t4;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;


public class TestDatetimes {
    public static void main(String[] args) throws ParseException {
        //1.java.util.Date 当前系统时间
//        System.out.println(new java.util.Date());
//
//        //自定义一个时间
//        String str = "1999-09-09";
//        //日期转换   字符串转为 java.util.Date
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        
//        //将日期字符串转换成 util.Date类型
//        java.util.Date utilDate  = sdf.parse(str);
//        System.out.println(utilDate);
//
//        //sql.Date  需要毫秒值,来构建一个日期
//        java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
//        System.out.println(sqlDate);
//
//
//        java.util.Date date = DateUtils.strToUtilDate("2002-3-18");
//        System.out.println(date);
//
//        java.sql.Date date2 = DateUtils.utilToSql(date);
//        System.out.println(date2);

        System.out.println(new java.util.Date());

        System.out.println(new java.sql.Date(new java.util.Date().getTime()));
    }
}

作业:user\userinfo

列名 类型 说明
user_id 整数、主键 用户编号
user_name 字符串,唯一,非空 用户名称
user_pwd 字符串,非空 用户密码
user_borndate DATE 出生日期
user_email 字符串,非空 邮箱
user_address 字符串 地址

注意:采用DAO+Entity完成

com.qf.xxx.entity

	User

com.qf.xxx.dao

	UserDaoImpl

			完成五个方法、增、删、改、查、查所有

十四、连接池


JDBC每次连接数据库,都要获得一个连接对象。每次创建一个连接对象,都是一个较大的资源,如果在连接量较大的场景下,会极大的浪费资源。容易内存溢出。

14.1 自定义连接池

Java中提供了一个接口DataSource,通过实现该接口,可以创建连接池

14.2 Druid(德鲁伊)

Druid 是目前比较流行高性能的,分布式列存储

一、亚秒级查询

二、实时数据注入

三、可扩展的PB级存储

四、多环境部署

五、丰富的社区

14.2.1 Druid配置

  • 创建database.properties 配置文件
  • 引入druid-1.1.5.jar

14.2.2 database.properties 文件配置

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/userinfo?useUnicode=true&characterEncoding=utf8
username=root
password=123456
#初始化连接
initialSize=10
#最大连接数量
maxActive=30
#最小空闲连接
minIdle=5
#超时等待时间以毫秒为单位
maxWait=5000

14.2.3 连接池工具类

package com.qf.day44.t2;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.xml.transform.Result;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DBPoolUtils {
    //连接池对象
    private static DruidDataSource ds;

    static {
        Properties properties = new Properties();
        InputStream is = DBPoolUtils.class.getResourceAsStream("/database.properties");
        try {
            properties.load(is);
            //使用德鲁伊工厂创建连接池
            ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {
            //在连接池中获得Connection
            return ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
  • 注意:连接池中获得的Connection是DruidPooledConnection实现类,调用的close()方法不是关闭资源,而是将资源放回池中!

十五、Service(Biz/Business)


15.1 业务

概念:用户要完成的一个业务功能,是由一个或多个的DAO调用组成。

软件、程序提供的一个功能都叫业务

15.2 业务层的实现

package com.qf.day44.t3;

/**
 * Userinfo的业务逻辑层对象
 */
public class UserinfoServiceImpl {

    /**
     * 用户的注册功能(业务)
     */
    public String register(Userinfo userinfo){//1.接收参数
        UserinfoDaoImpl userinfoDao = new UserinfoDaoImpl();

        //2.调用数据访问层对象的查询方法!
        Userinfo check = userinfoDao.select(userinfo.getUser_name());
        if(check!=null){//用户存在!~
            return "用户名已存在!";
        }
        //3.调用数据访问层对象的新增方法
        int result = userinfoDao.insert(userinfo);
        //4.将操作结果返回给调用者
        if(result >0 ){
            return "注册成功!";
        }else{
            return "注册失败!";
        }
    }
    /**
    * 登录功能业务
    */
    public Userinfo login(String user_name,String user_pwd){//收参
        UserinfoDaoImpl userinfoDao = new UserinfoDaoImpl();
        //2.调用数据访问层对象的查询方法
        Userinfo userinfo = userinfoDao.select(user_name);

        //3.接收结果,处理结果
        if(userinfo!=null){//用户存在!
            //检验查询到的用户密码和输入的密码是否一致!
            if(userinfo.getUser_pwd().equals(user_pwd)){
                return userinfo;
            }
        }
        //4.响应给调用者结果
        return null;
    }
}

15.3 复用

  • DAO数据访问操作复用
  • 业务功能的复用 //不同的终端访问

15.4 转账案例

代码参考Day44下的accounts项目

15.5 解决转账事务问题

1、传递Connection:如果将Service获得的Connection对象,传递给DAO各个方法。可以。//BadSmell 臭味

											定义接口是为了更容易更换实现!而将Connection参数定义在接口方法中,就会污染当前接口,而无法复用。JDBC-Connection。MyBatis使用SqlSession

2、单例:当前项目只能有一个客户端连接

十六、ThreadLocal


线程工具类:在整个线程中,一直到释放资源,用的是同一个Connection连接对象。

16.1 ThreadLocal

1、在整个线程(单条执行路径中)所持有的Map中,存储一个键(threadlocal)值(connection)对

2、线程(Thread)对象中持有一个ThreadLocalMap类型的对象(ThreadthreadLocals),threadLocals中保存了以ThreadLocal对象为Key,set进去的值为Value

3、每个线程均可绑定多个ThreadLocal,一个线程中可存储多个ThreadLocal

16.1.1 ThreadLocal代码

//绑定到线程中! 绑定到当前线程中
        ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();//0x112233

//        Thread
        //获得当前线程对象-->t.threadLocals集合为空-->create-->table[entry]-->key=0x112233 value=connection
        threadLocal.set(null);
        //获得当前线程对象-->getMap--->t.threadLocals-->getEntry(0x112233)-->entry-->entry.value
        Connection connection = threadLocal.get();

        ThreadLocal<Integer> threadLocal1 = new ThreadLocal<Integer>();//0x2345

        //每个线程可以绑定多个ThreadLocal,
        threadLocal1.set(123);

        Integer i = threadLocal1.get();
        System.out.println(i);

16.2 ThreadLocal事务控制优化

将业务层的多步事务控制操作,封装在DBUtils工具类里。实现复用

16.2.1 DBUtils封装事务控制

  //开启事务
    public static void begin(){
        Connection connection = getConnection();
        try {
            connection.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交事务
    public static void commit(){
        Connection connection = getConnection();
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(connection,null,null);
        }
    }
    //回滚事务
    public static void rollback(){
        Connection connection = getConnection();
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(connection,null,null);
        }
    }

十七、三层架构设计


  • 表示层:
    • 命名:xxxVIew
    • 职责:收集用户的数据和需求、展示数据
  • 业务逻辑层
    • 命名:XXXServiceImpl
    • 职责:数据的加工处理、调用Dao组合完成业务实现、控制事务
  • 数据访问层
    • 命名:xxxDaoImpl
    • 职责:向业务层提供数据,将业务层加工处理后的数据同步到数据库

十八、工具类型的封装及普适性泛型工具


18.1 封装DML方法

public int commonsUpdate(String sql, Object... args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);

            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }

            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(null, preparedStatement, null);
        }
        return 0;
    }

18.2 封装DQL方法

 /**
     * 公共查询方法 (可查询单个对象,也可查询多个对象,可以查任何一张表)
     *
     * @param sql
     * @param args
     * @return
     */
    //                              select * from t_account
    //                              select * from t_student
    //工具不知道查的是什么  调用者知道
    //封装对象、对象赋值  调用者清楚
    public List<T> commonsSelect(String sql, RowMapper<T> rowMapper, Object... args) {

        List<T> elements = new ArrayList<T>();

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);

            if(args !=null){
                for (int i = 0; i < args.length; i++) {
                    preparedStatement.setObject(i + 1, args[i]);
                }
            }


            resultSet = preparedStatement.executeQuery();

            while (resultSet.next()) {
                //根据查询到的结果完成ORM,如何进行对象的创建及赋值?
                T t = rowMapper.getRow(resultSet);//回调---->调用者提供的一个封装方法ORM
                elements.add(t);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(null, preparedStatement, resultSet);
        }
        return elements;
    }

十九、Apache的DbUtils使用


Commons DbUtils 是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能勾简化JDBC应用程序的开发!同时,不会影响程序的性能

19.1 DbUtils简介

  • DbUtils是Java编程中数据库操作实用小工具,小巧、简单、实用
    • 对于数据表的查询操作,可以吧结果转换为List、Array、Set等集合。便于操作
    • 对于数据表的DML操作,也变得很简单(只需要写SQL语句);

19.1.1 DbUtils主要包含

  • ResultSetHandler接口:转换类型接口
    • BeanHandler类:实现类,把一条记录转换成对象
    • BeanListHandler类:实现类,把多条记录转换成List集合。
    • ScalarHandler类:实现类,适合获取一行一列的数据。
  • QueryRunner:执行sql语句的类
    • 增、删、改:update();
    • 查询:query();

19.2 DbUtils的使用步骤

  • 导入jar包
    • mysql连接驱动jar包
    • druid-1.1.5.jar
    • database.properties配置文件
    • commons-dbutils-1.6.jar

19.2.1 代码实现

DBUtils工具类

package com.project.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 连接池工具类
 */
public class DBUtils {
    private static DruidDataSource dataSource;

    static {
        Properties properties = new Properties();
        InputStream is = DBUtils.class.getResourceAsStream("/database.properties");
        try {
            properties.load(is);
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 返回一个数据源
    public static DataSource getDataSource(){
        return dataSource;
    }
}

UserDaoImpl 数据访问对象

package com.project.dao.impl;

import com.project.dao.UserDao;
import com.project.entity.User;
import com.project.utils.DBUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl implements UserDao {
    //1.创建QueryRunner对象,并传递一个数据源对象
    private QueryRunner queryRunner = new QueryRunner(DBUtils.getDataSource());
    @Override
    public int insert(User user) {
        Object[] params={user.getId(),user.getUsername(),user.getPassword(),user.getSex(),user.getEmail(),user.getAddress()};
        try {
            return queryRunner.update("insert into user (id,username,password,sex,email,address) values(?,?,?,?,?,?)",params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int update(User user) {
        Object[] params={user.getUsername(),user.getPassword(),user.getSex(),user.getEmail(),user.getAddress(),user.getId()};
        try {
            return queryRunner.update("update user set username=?,password=?,sex=?,email=?,address=? where id = ?",params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int delete(int id) {
        try {
            return queryRunner.update("delete from user where id = ?",id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public User select(int id) {
        try {
            //把查询到的记录封装成 指定对象
            return queryRunner.query("select * from user where id = ?", new BeanHandler<User>(User.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询所有
     * @return
     */
    @Override
    public List<User> selectAll() {
        try {
            return queryRunner.query("select * from user;",new BeanListHandler<User>(User.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}
发布了24 篇原创文章 · 获赞 1 · 访问量 709

猜你喜欢

转载自blog.csdn.net/weixin_46286064/article/details/105307874
今日推荐