1 JDBC

JDBC

单元测试

常用注解:

  • @Test:用于修饰需要执行的方法
  • Before:测试方法前执行的方法
  • After:测试方法执行后的方法

JDBC

  • JDBC是接口,驱动是接口的实现
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n8vsr5Ui-1582813136402)(:storage\bf61bd7e-df00-4187-9d38-7cc30fb2739c\4f632fbf.png)]
  • 核心类
    • DriverManager
      • 注册驱动
      • 获取连接
    • Connection
    • Statement
    • ResulSet

开发步骤

  1. 注册驱动

Class.forName(“com.myssql.jdbc.Driver”);

  1. 获取连接

Connection conn = DriverManager.getConnection(url);

  • url
    • 通用:jdbc:mysql://localhost:3306/database","count","passwd"
    • 扩展:通用url?useUnicode=true&characerEncoding=UTF8(不是UTF-8)
      • useUnicode=true:使用Unicode字节集
      • characerEncoding=UTF8:Java程序连接数据库的过程中,使用的字节集为UTF-8
  1. 获得语句执行者

Statement st = conn.createStatement()

  • Statement createStatement(int resultSetType, int resultSetConcurrency)
    • resultSetType的可选值
      • ResultSet.TYPE_FORWARD_ONLY:不滚动结果集
      • ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,但结果集数据不会再跟随数据库而变化
      • ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,但结果集数据会跟随数据库而变化
    • resultSetConcurrency的可选值
      • CONCUR_READ_ONLY:结果集是只读的,不能通过修改结果集而反向影响数据库
      • CONCUR_UPDATABLE:结果集是可更新的,对结果集的更新可以反向影响数据库
  1. 执行SQL语句

ResultSet rs = st.executeQuery()

语句说明
int executeUpdate(String sql)执行insert、update、delete
ResultSet executeQuery(String sql)select语句
bollean execute(String sql)执行select返回true,其他语句返回false;如果true,需要使用getResultSet()获取结果;如果false,需要使用getUpdateCount()获取影响行数
addBatch(String sql); clearBatch();executeBatch();执行批处理
  1. 处理结果
    ResultSet方法
方法说明
void beforeFirst()把光标放到第一行前面
void afterLast()把光标放到最后一行
boolean first()把光标放到第一行的位置上
boolean last()把光标放到最后一行的位置上
boolean isBeforeFirst()当前光标位置是否在第一行前面
boolean isAfterLast()当前光标位置是否在最后一行的后面;
boolean isFirst()当前光标位置是否在第一行上;
boolean isLast()当前光标位置是否在最后一行上;
boolean previous()把光标向上挪一行;
boolean next()把光标向下挪一行;
boolean relative(int row)相对位移,当row为正数时,表示向下移动row行,为负数时表示向上移动row行;
boolean absolute(int row)绝对位移,把光标移动到指定的行上;
int getRow()返回当前光标所在行
  • 上面方法分为两类,一类用来判断游标位置的,另一类是用来移动游标的。如果结果集是不可滚动的,那么只能使用next()方法来移动游标,而beforeFirst()、afterLast()、first()、last()、previous()、relative()方法都不能使用

  • 获取列数据

方法说明
String getString(int columnIndex)获取指定列的String类型数据;
int getInt(int columnIndex)获取指定列的int类型数据;
double getDouble(int columnIndex)获取指定列的double类型数据;
boolean getBoolean(int columnIndex)获取指定列的boolean类型数据;
Object getObject(int columnIndex)获取指定列的Object类型的数据
String getString(String columnName)获取名称为columnName的列的String数据
int getInt(String columnName)获取名称为columnName的列的int数据
double getDouble(String columnName)获取名称为columnName的列的double数据
boolean getBoolean(String columnName)获取名称为columnName的列的boolean数据
Object getObject(String columnName)获取名称为columnName的列的Object数据

PreparedStatement

  • sql攻击
    在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段

  • 防止SQL攻击
     过滤用户输入的数据中是否包含非法字符;
     分步校验!先使用用户名来查询用户,如果查找到了,再比较密码;
     使用PreparedStatement

  • preparedStatement:预编译声明,是Statement的子接口

    • 使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定
    • 调用PreparedStatement的setXXX()系列方法为问号设置值
    • 调用executeUpdate()或executeQuery()方法
    String sql = “select * from tab_student where s_number=?;
    PreparedStatement pstmt = con.prepareStatement(sql);
    
    pstmt.setString(1, “S_1001”);
    ResultSet rs = pstmt.executeQuery();
    
    rs.close();
    pstmt.clearParameters();
    
    pstmt.setString(1, “S_1002”);
    rs = pstmt.executeQuery();
    
    

完整代码


try {
      //注册驱动,com.mysql.jdbc.Driver已被弃用
      Class.forName("com.mysql.cj.jdbc.Driver");
      //获得链接
      Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","1234567890");
      //获取语句执行者
      Statement st = conn.createStatement();
      //获取结果集
      ResultSet rs = st.executeQuery("select * from category");
      //获取结果
      while(rs.next()){
        String cid = rs.getString("cid");
        String cname = rs.getString(2);

        System.out.println(cid+":"+cname);

      }

      rs.close();
      st.close();
      conn.close();
    } catch (Exception e) {
      // 异常获取
      e.printStackTrace();
    }
//规范代码,无论是否出现异常,都要关闭ResultSet、Statement、Connection
  Connection con = null;
  	Statement stmt = null;
  	ResultSet rs = null;
  	try {
  		con = getConnection(url);
  		stmt = con.createStatement();
  		String sql = "select * from user";
  		rs = stmt.executeQuery(sql);
  		while(rs.next()) {
  			String username = rs.getString(1);
  			String password = rs.getString(2);
  			System.out.println(username + ", " + password);
  		}
  	} catch(Exception e) {
  		throw new RuntimeException(e);
  	} finally {
  		try {
  			if(rs != null) rs.close();
  			if(stmt != null) stmt.close();
  			if(con != null) con.close();
  		} catch(SQLException e) {}
  	}

可能存在的问题

  • mysql时区报错
    myslq默认是美国的时区
    在mysql安装目录下,打开mysql.ini文件,添加default-time-zone=’+08:00’字样,然后,一定要重新启动mysql服务

生产环境中的JDBC–properties配置文件

  • 创建后缀为properties的文件
  • 文件内容
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=...
    jdbc.user=...
    jdbc.password=...
    
  • 使用ResourceBundle加载文件
    private static driver;
    ...
    
    ResourceBundle rb = ResourceBundle.getBundle(文件名);
    driver = bundle.getString(jdbc.driver);
    ...
    
  • Properties对象
    //假定当前类为jdbcutils
    //1. 使用类加载ClassLoader加载src资源
    InputStream is = jdbcutils.class.getClassLoader().getResourceAsStream(配置文件名)//2. 加载当前类同包下的资源,如果需要从src资源开始必须填写/
    InputStream is2 = jdbcutils.class.getClassLoader().getResourceAsStream(/配置文件名)//使用properties处理流
    Properties props = new Properties();
    props.load(is);
    
    //调用getProperty(key)
    driver = props.getProperty("jdbc.driver");
    ....
    
    

JDBC连接池

  • 获得连接和释放资源非常耗费系统资源

自定义连接池

  • 实现java.sql.DataSource接口
  • 使用list存放多个连接对象
      public class MyDataSource implements DataSource{
      // 创建一个List集合用于存放多个连接对象.
      private List<Connection> list = new ArrayList<Connection>();
      // 在程序开始的时候,初始化几个连接,将连接存放到list中.
      //可以将初始化过程放在static代码块中
      public MyDataSource() {
        // 初始化3个连接:
        for(int i=1;i<=3;i++){
          try{
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection conn = DriverManager.getConnection(url);
            list.add(conn);      
          }
          catch(Exception e){
            throw new RuntimeException(e);
          }
        }
      }
    
      @Override
      // 获得连接的方法:
      public Connection getConnection() throws SQLException {
        if(list.size() <= 0){
          //进行连接创建
          ....
        }
        Connection conn = list.remove(0);
        return conn;
      }
    
      // 归还连接的方法:
      public void addBack(Connection conn){
        list.add(conn);
      }
    ...
    }
    
  • 存在的问题
    • 如果用户没有将连接归还而是直接调用close关闭,连接池中将出现无连接可用
    • 解决
      • 继承,控制类的构造
      • 装饰者模式:包装对象和被包装对象实现相同的接口
      • 动态代理
      //继承与装饰模式案例
      /**
       * 继承的方式增强一个类中某个方法:
       */
      class Man{
        public void run(){
          System.out.println("跑....");
        }
      }
      
      class SuperMan extends Man{
        public void run(){
          // super.run();
          System.out.println("飞....");
        }
      }
      
      /**
       * 使用装饰者的方式完成类的方法的增强
       */
      interface Waiter{
        public void server();
      }
      
      class Waiteress implements Waiter{
      
        @Override
        public void server() {
          System.out.println("服务...");
        }
      
      }
      
      class WaiteressWrapper implements Waiter{
          private Waiter waiter;
      
        public WaiteressWrapper(Waiter waiter) {
            this.waiter = waiter;
        }
      
        @Override
        public void server() {
          System.out.println("微笑...");
          // this.waiter.server();
      
        }
      
      }
      
      
      //改善后的连接池
      public class MyConnection implements Connection{
      
      private Connection conn;
      private List<Connection> list;
      
      public MyConnection(Connection conn,List<Connection> list) {
        this.conn = conn;
        this.list = list;
      }
      
      
      @Override
      public void close() throws SQLException {
        list.add(conn);
      }
         ...
      }
      
      连接池的getConnection方法:
      @Override
      // 获得连接的方法:
      public Connection getConnection() throws SQLException {
        if(list.size() <= 0){
          for(int i=1;i<=3;i++){
            Connection conn = JDBCUtils.getConnection();
            list.add(conn);
          }	
        }
        Connection conn = list.remove(0);
        MyConnection myConn = new MyConnection(conn, list);
        return myConn;
      }
      

DBCP连接池

  • 引入DBCP连接池的jar包
  • 编写DBCP代码
    • 设置参数
    • 配置文件设置参数
      //手动方式
      Connection conn = null;
      	PreparedStatement stmt = null;
      	ResultSet rs = null;
      	BasicDataSource dataSource = new BasicDataSource();
      	dataSource.setDriverClassName("com.mysql.jdbc.Driver");
      	dataSource.setUrl("jdbc:mysql:///web_07");
      	dataSource.setUsername("root");
      	dataSource.setPassword("123");
      	try{
      		// 获得连接:
      		conn = dataSource.getConnection();
      		// 编写SQL:
      		String sql = "select * from category";
      		// 预编译SQL:
      		stmt = conn.prepareStatement(sql);
      		// 执行SQL:
      		rs = stmt.executeQuery();
      		while(rs.next()){
      			System.out.println(rs.getInt("cid")+"   "+rs.getString("cname"));
      		}
      	}catch(Exception e){
      		e.printStackTrace();
      	}finally{
      		JDBCUtils.release(rs,stmt, conn);
      	}
        
      //----------------配置文件方式-------------------------
      Connection conn = null;
      	PreparedStatement stmt = null;
      	ResultSet rs = null;
      	Properties properties = new Properties();
      	
      	try{
      		properties.load(new FileInputStream("src/dbcpconfig.properties"));
      		DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
      		// 获得连接:
      		conn = dataSource.getConnection();
      		// 编写SQL:
      		String sql = "select * from category";
      		// 预编译SQL:
      		stmt = conn.prepareStatement(sql);
      		// 执行SQL:
      		rs = stmt.executeQuery();
      		while(rs.next()){
      			System.out.println(rs.getInt("cid")+"   "+rs.getString("cname"));
      		}
      	}catch(Exception e){
      		e.printStackTrace();
      	}finally{
      		JDBCUtils.release(rs,stmt, conn);
      	}
      

C3P0

  • 与DBCP相似
    配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    
      <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///web08</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">20</property>
      </default-config>
    
      <named-config name="itheima">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///web08</property>
        <property name="user">root</property>
        <property name="password">root</property>
      </named-config>
    
    
    </c3p0-config>
    
    public class JDBCUtils2 {
      private static final ComboPooledDataSource DATA_SOURCE =new ComboPooledDataSource();
      /**
       * 获得连接的方法
       */
      public static Connection getConnection(){
        Connection conn = null;
        try {
          conn = DATA_SOURCE.getConnection();
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return conn;
      }
    ...
    

JavaBean

  • 属性
    • 需要实现接口java.io.Serializable,通常省略
    • 提供私有字段
    • 提供getter、setter方法
    • 提供无参构造

DButils

  • 核心功能

    • QueeryRunner提供对sql语句操作的API
    • ResultSetHandle接口,定义select操作后,怎样粉装结果集
    • DbUtils类,定义了关闭资源与事务处理的方法
  • QueryRunner核心类

    • QueeryRunner(DataSource ds),提供数据源(连接池),底层自动维护连接connection
    • update(String sql,object … params),执行更新数据
    • query(string sql,ResultSetHandle<T> rsh ,Object …params),执行查询
  • ResultSetHandle
    ArrayHandler:将结果集的第一条记录封装到一个数组中

    ArrayListHandler:将结果集中的每一条记录都封装到一个object数组中,将这些数组封装到list集合中

    MapHandler:单行处理器!把结果集转换成Map<String,Object>,其中列名为键!

    MapListHandler:多行处理器!把结果集转换成List<Map<String,Object>>;

    BeanHandler:单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型;

    BeanListHandler:多行处理器!把结果集转换成List<Bean>;

    ColumnListHandler:多行单列处理器!把结果集转换成List<Object>,使用ColumnListHandler时需要指定某一列的名称或编号

    ScalarHandler:单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQ2ROJEg-1582813136442)(:storage\bf61bd7e-df00-4187-9d38-7cc30fb2739c\c15ec651.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOHHph6Y-1582813136443)(:storage\bf61bd7e-df00-4187-9d38-7cc30fb2739c\adfbd723.png)]

@Test
  public void fun1() throws SQLException {
    DataSource ds = JdbcUtils.getDataSource();
    QueryRunner qr = new QueryRunner(ds);
    String sql = "select * from tab_student where number=?";
    Map<String,Object> map = qr.query(sql, new MapHandler(), "S_2000");
    System.out.println(map);
  }

  @Test
  public void fun2() throws SQLException {
    DataSource ds = JdbcUtils.getDataSource();
    QueryRunner qr = new QueryRunner(ds);
    String sql = "select * from tab_student";
    List<Map<String,Object>> list = qr.query(sql, new MapListHandler());
    for(Map<String,Object> map : list) {
      System.out.println(map);
    }
  }

  @Test
  public void fun3() throws SQLException {
    DataSource ds = JdbcUtils.getDataSource();
    QueryRunner qr = new QueryRunner(ds);
    String sql = "select * from tab_student where number=?";
    Student stu = qr.query(sql, new BeanHandler<Student>(Student.class), "S_2000");
    System.out.println(stu);
  }

  @Test
  public void fun4() throws SQLException {
    DataSource ds = JdbcUtils.getDataSource();
    QueryRunner qr = new QueryRunner(ds);
    String sql = "select * from tab_student";
    List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));
    for(Student stu : list) {
      System.out.println(stu);
    }
  }

  @Test
  public void fun5() throws SQLException {
    DataSource ds = JdbcUtils.getDataSource();
    QueryRunner qr = new QueryRunner(ds);
    String sql = "select * from tab_student";
    List<Object> list = qr.query(sql, new ColumnListHandler("name"));
    for(Object s : list) {
      System.out.println(s);
    }
  }

  @Test
  public void fun6() throws SQLException {
    DataSource ds = JdbcUtils.getDataSource();
    QueryRunner qr = new QueryRunner(ds);
    String sql = "select count(*) from tab_student";
    Number number = (Number)qr.query(sql, new ScalarHandler());
    int cnt = number.intValue();
    System.out.println(cnt);
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值