新职课(chapter5-1)

数据库

  • 基础原理就不讲了,直接看Java中的操作
  • 大部分数据库操作都是查询,复杂查询按照套路走
    • 合并查询
    • 内连查询
    • 外连查询

JDBC

  • Java应用通过JDBC API调用驱动,操作对应的数据库
    在这里插入图片描述

  • 核心组件
    在这里插入图片描述

  • 使用步骤(六步)

    • 导包:import java.sql.*
    • 初始化驱动
    • 连接:DriverManager.getConnection()
    • 执行查询
    • 提取数据
    • 关闭连接
    // 在project structure中创建Library引入jar包
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        ResultSet result = null;
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");  // 反射获取驱动类
            String username = "root";
            String password = "123456";
            String url = "jdbc:mysql://localhost:3306/flask?serverTimezone=UTC";    // 数据库名称 falsk, 注意格式
            // 2. 获取连接
            conn = DriverManager.getConnection(url, username, password);   // Home End
            // 3. 定义SQL
            stat = conn.createStatement();
            result = stat.executeQuery("select * from short_url;");
            // int effect = stat.executeUpdate("insert into short_url(token, url) values('test2', 'http://porn.com')");// 插入,返回受影响行数
            // 4. 取结果
            while (result.next()) {
                System.out.println("id:"+result.getString("id")+
                        " token:"+result.getString("token"));
            }
            // 如果是增删改:
            // if (effect > 0) {
            //     System.out.println("执行成功!");
            // }else {
            //     System.out.println("执行失败!");
            // }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 5. 关闭连接
            try {
                if (result != null) {
                    result.close();
                }
                if (stat != null) {
                    stat.close();
                }
                if (conn != null) {
                    conn.close();   // 倒序关闭
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    

SQL注入

  • 在执行的SQL语句中加入一些特殊字符,让引擎误识别,能绕开权限获取数据,造成数据的不安全
  • 避免SQL注入的方法:创建预状态通道
  • 在使用JDBC的第三步,通过createStatement创建的是状态通道,现在使用PreparedStatement创建预通道
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        PreparedStatement pstmt = null;
        ResultSet result = null;
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");  // 反射获取驱动类
            String username = "root";
            String password = "123456";
            String url = "jdbc:mysql://localhost:3306/flask?serverTimezone=UTC";    // 数据库名称 falsk, 注意格式
            // 2. 获取连接
            conn = DriverManager.getConnection(url, username, password);   // Home End
            // 3. 定义SQL并执行
    		// stat = conn.createStatement();
            String sql = "select * from short_url where id=?";  // 所有的参数使用 ? 占位
            pstmt = conn.prepareStatement(sql);
            String id = "1";
            // 替换占位符
            pstmt.setString(1, id); // 使用字符串
            result = pstmt.executeQuery();
            // result = stat.executeQuery("select * from short_url;"); // 查询
            // 4. 取结果
            while (result.next()) {
                System.out.println("id:"+result.getString("id")+
                        " token:"+result.getString("token"));
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 5. 关闭连接
            try {
                if (result != null) {
                    result.close();
                }
                if (pstmt != null) {
                    stat.close();
                }
                if (conn != null) {
                    conn.close();   // 倒序关闭
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    
    • 预状态通道会先编译sql语句,再去执行,比statement执行效率高
    • 预状态通道支持占位符?,给占位符赋值的时候,位置从1开始
    • 预状态通道可以防止sql注入,原因:预状态通道在处理值的时候全部以字符串的方式处理

两表关系

  • 数据库通过外键建立两表关系
  • 实体类通过属性的方式建立两表关系,类似对象关系映射ORB
    • 类名=表名
    • 属性名=字段名
  • 使用下面的数据表练习
    CREATE TABLE `student` (
    `stuid` int(11) NOT NULL AUTO_INCREMENT,
    `stuname` varchar(255) DEFAULT NULL,
    `teacherid` int(11) DEFAULT NULL,
    PRIMARY KEY (`stuid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    INSERT INTO `student` VALUES ('1', 'aaa', '3');
    INSERT INTO `student` VALUES ('2', 'bb', '1');
    INSERT INTO `student` VALUES ('3', 'cc', '3');
    INSERT INTO `student` VALUES ('4', 'dd', '1');
    INSERT INTO `student` VALUES ('5', 'ee', '1');
    INSERT INTO `student` VALUES ('6', 'ff', '2');
    DROP TABLE IF EXISTS `teacher`;
    CREATE TABLE `teacher` (
    `tid` int(11) NOT NULL AUTO_INCREMENT,
    `tname` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`tid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    INSERT INTO `teacher` VALUES ('1', '张三老师');
    INSERT INTO `teacher` VALUES ('2', '李四老师');
    INSERT INTO `teacher` VALUES ('3', '王五');
    -- teacher表是主表,一个tid对应多个学生,所以后面的Java代码中要多加个集合属性
    
    5
  • 接下来创建对应的类和属性,显然,这些类都是bean
    public class Student {
        /*
        学生表是多端,或者说子表
         */
        private int stuid;
        private String stuname;
        // 外键列一般不生成方法
        private int teacherid;
    
    
        public int getStuid() {
            return stuid;
        }
    
        public void setStuid(int stuid) {
            this.stuid = stuid;
        }
    
        public String getStuname() {
            return stuname;
        }
    
        public void setStuname(String stuname) {
            this.stuname = stuname;
        }
    }
    
    public class Teacher {
        /*
        教师表是一端,受学生表外键的约束
         */
        private int tid;
        private String tname;
        private List<Student> list=new ArrayList<Student>(); // 新增属性,一对多查询,保存学生信息
    
        public int getTid() {
            return tid;
        }
    
        public void setTid(int tid) {
            this.tid = tid;
        }
    
        public String getTname() {
            return tname;
        }
    
        public void setTname(String tname) {
            this.tname = tname;
        }
    
        public List<Student> getList() {
            return list;
        }
    
        public void setList(List<Student> list) {
            this.list = list;
        }
    }
    
  • 创建dao层,定义一对多的查询接口和实现类(一个老师对应多个学生)
    3
    // TeacherDaoImpl.java
    public class TeacherDaoImpl implements TeacherDao {
        @Override
        public Teacher getById(int tid) {
            Connection conn = null;
            PreparedStatement pstmt = null;
            ResultSet result = null;
            try {
                // 1. 加载驱动
                Class.forName("com.mysql.cj.jdbc.Driver");  // 反射获取驱动类
                String username = "root";
                String password = "123456";
                String url = "jdbc:mysql://localhost:3306/flask?serverTimezone=UTC";    // 数据库名称 falsk, 注意格式
                // 2. 获取连接
                conn = DriverManager.getConnection(url, username, password);   // Home End
                // 3. 定义SQL并执行  预通道
                String sql = "select * from student s, teacher t where s.teacherid=t.tid and tid=?";  // 所有的参数使用 ? 占位
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, tid); // 都要使用字符串,数据库引擎会解析
                result = pstmt.executeQuery();
                // 4. 取结果
                Teacher teacher = new Teacher();
                List<Student> students = new ArrayList<Student>();
                while (result.next()) {
                    // 取出各自信息,注意顺序
                    teacher.setTid(result.getInt("tid"));
                    teacher.setTname(result.getString("tname"));
    
                    Student student = new Student();
                    student.setStuid(result.getInt("stuid"));
                    student.setStuname(result.getString("stuname"));
    
                    students.add(student);
                }
                teacher.setList(students);
                return teacher;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                // 5. 关闭连接
                try {
                    if (result != null) {
                        result.close();
                    }
                    if (pstmt != null) {
                        pstmt.close();
                    }
                    if (conn != null) {
                        conn.close();   // 倒序关闭
                    }
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            return null;
        }
    }
    
    // test/Teac.java
    public static void main(String[] args) {
       // 操作数据库都会定义一个dao层,这里的dao和sql包,类似MVC模型的M
        TeacherDao daot = new TeacherDaoImpl(); // Impl是具体实现,类似List和ArrayList
        Teacher teacher = daot.getById(1);
        System.out.println("老师姓名:"+teacher.getTname());
        // 一查多
        List<Student> students = teacher.getList();
        for (Student student:students) {
            System.out.println("\tsname: "+student.getStuname());
        }
    }
    
  • 多对一查询(每个学生都带上老师信息),也是类似的,在Student类中增加属性保存老师信息呗
    // 增加属性,多对一查询
    private Teacher teacher;
    
    public Teacher getTeacher() {
       return teacher;
    }
    
    public void setTeacher(Teacher teacher) {
       this.teacher = teacher;
    }
    
    • 一对多:一方中有一个多方的集合
    • 多对一:多方中有一个一方的数据
  • 一对一:用的比较少,就是写SQL的区别,其他一样,两个表都要新增保存对方数据的属性
  • 多对多,例如学生和科目,用到如下表:
    CREATE TABLE `middle` (
    `middleid` int(11) NOT NULL AUTO_INCREMENT,
    `stuid` int(11) DEFAULT NULL,
    `subid` int(11) DEFAULT NULL,
    PRIMARY KEY (`middleid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
    
    INSERT INTO `middle` VALUES ('1', '1', '1');
    INSERT INTO `middle` VALUES ('2', '1', '2');
    INSERT INTO `middle` VALUES ('3', '1', '3');
    INSERT INTO `middle` VALUES ('4', '1', '5');
    INSERT INTO `middle` VALUES ('5', '2', '2');
    INSERT INTO `middle` VALUES ('6', '3', '2');
    INSERT INTO `middle` VALUES ('7', '4', '2');
    INSERT INTO `middle` VALUES ('8', '5', '2');
    INSERT INTO `middle` VALUES ('9', '6', '2');
    
    DROP TABLE IF EXISTS `student`;
    CREATE TABLE `student` (
    `stuid` int(11) NOT NULL AUTO_INCREMENT,
    `stuname` varchar(255) DEFAULT NULL,
    `teacherid` int(11) DEFAULT NULL,
    PRIMARY KEY (`stuid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    
    INSERT INTO `student` VALUES ('1', '张三', '3');
    INSERT INTO `student` VALUES ('2', '李四', '1');
    INSERT INTO `student` VALUES ('3', '王五', '3');
    INSERT INTO `student` VALUES ('4', '赵六', '1');
    INSERT INTO `student` VALUES ('5', '花花', '1');
    INSERT INTO `student` VALUES ('6', '潇潇', '2');
    
    DROP TABLE IF EXISTS `subject`;
    CREATE TABLE `subject` (
    `subid` int(11) NOT NULL AUTO_INCREMENT,
    `subname` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`subid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    
    INSERT INTO `subject` VALUES ('1', 'java');
    INSERT INTO `subject` VALUES ('2', 'ui');
    INSERT INTO `subject` VALUES ('3', 'h5');
    INSERT INTO `subject` VALUES ('4', 'c');
    INSERT INTO `subject` VALUES ('5', 'c++');
    INSERT INTO `subject` VALUES ('6', 'c#');
    
    • 多对多要用到中间表middle;创建对应的类
    • 在Student类和Subject类中增加相应的属性配置
    // 配置多对多,增加属性
    private List<Subject> subjects;
    
    public List<Subject> getSubjects() {
        return subjects;
    }
    
    public void setSubjects(List<Subject> subjects) {
        this.subjects = subjects;
    }
    
    public class Subject {
        private int subid;
        private String subname;
    
        // 配置多对多,新增学生集合
        private List<Student> students;
    
        public List<Student> getStudents() {
            return students;
        }
    
        public void setStudents(List<Student> students) {
            this.students = students;
        }
    
        public int getSubid() {
            return subid;
        }
    
        public void setSubid(int subid) {
            this.subid = subid;
        }
    
        public String getSubname() {
            return subname;
        }
    
        public void setSubname(String subname) {
            this.subname = subname;
        }
    }
    
    • dao层定义接口和实现类,并测试
    // 代码很长,这里就放两个SQL
    // select * from student s, subject sub, middle m where s.stuid=m.stuid and sub.subid=m.subid and s.stuid=?
    // select * from subject sub, student s, middle m where s.stuid=m.stuid and sub.subid=m.subid and s.subid=?
    // 剩下的就是JDBC五步骤,分开来看还是用中间表实现了,一对多查询
    

事务

  • 事务特点:ACID(背)
    4
  • 一般设置手动提交
    // 获取连接时
    conn.setAutoCommit(false);  // 不自动提交
    
  • 可以设置保存点 savepoints,回滚时可以指定到某个点,而不是全部失败
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        PreparedStatement pstmt = null;
        Savepoint sp1 = null;
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");  // 反射获取驱动类
            String username = "root";
            String password = "123456";
            String url = "jdbc:mysql://localhost:3306/flask?serverTimezone=UTC";
            // 2. 获取连接
            conn = DriverManager.getConnection(url, username, password);
            conn.setAutoCommit(false);  // 不自动提交
            // 3. 定义SQL并执行
            String sql_a = "insert into middle(stuid, subid) values(6, 3)";
            pstmt = conn.prepareStatement(sql_a);
            int result = pstmt.executeUpdate();
            // 设置一个保存点------------------
            sp1 = conn.setSavepoint("sp1");
            // 手动失败-----------------------
            System.out.println(5/0);
            String sql_b = "insert into middle(stuid, subid) values(2, 3)";
            pstmt = conn.prepareStatement(sql_b);
            int result2 = pstmt.executeUpdate();
            // 手动提交事务
            conn.commit();
            // 4. 取结果
            if (result > 0) {
                System.out.println("执行成功!");
            } else {
                System.out.println("执行失败!");
            }
        } catch (Exception e) {
            // 注意这里要用父类Exception,不然不走这块的的异常
            e.printStackTrace();
            try {
                // conn.rollback(); // 一次性全部回滚
                conn.rollback(sp1);
                conn.commit();  // 这次提交的就是回滚点之前的那个插入操作
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        } finally {
            // 5. 关闭连接
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (conn != null) {
                    conn.close();   // 倒序关闭
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    
  • 用到事务的地方很多,比如转账,一个减钱,一个加钱

批处理

  • 批量更新数据库数据,我们在预通道方式下测试,相比通道就是多了一个传参的操作,不是直接传SQL到addBatch
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        Savepoint sp1 = null;
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");  // 反射获取驱动类
            String username = "root";
            String password = "123456";
            String url = "jdbc:mysql://localhost:3306/flask?serverTimezone=UTC";
            // 2. 获取连接
            conn = DriverManager.getConnection(url, username, password);
            conn.setAutoCommit(false);  // 不自动提交
            // 3. 定义SQL并执行
            String sql_a = "insert into teacher(tname) values(?)";
            pstmt = conn.prepareStatement(sql_a);
            // 批处理---------------
            pstmt.setString(1, "Roy");
            pstmt.addBatch();
            pstmt.setString(1, "Allen");
            pstmt.addBatch();
            pstmt.setString(1, "Andy");
            pstmt.addBatch();
            int[] result = pstmt.executeBatch();    // 返回每条语句影响的行数
            // 设置一个保存点
            sp1 = conn.setSavepoint("sp1");
            // 手动提交事务
            conn.commit();
            // 4. 取结果
            for (int i:result) {
                System.out.println(i);
            }
        } catch (Exception e) {
            // 注意这里要用父类Exception,不然不走这块的的异常
            e.printStackTrace();
            try {
                conn.rollback(sp1);
                conn.commit();  // 这次提交的就是回滚点之前的那个插入操作
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        } finally {
            // 5. 关闭连接
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (conn != null) {
                    conn.close();   // 倒序关闭
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    
  • 可以通过类反射执行上述所有的SQL操作,了解即可

工具类

  • 为了避免每次写一堆重复步骤,封装一个工具类,包含连接库、增删改查的方法
    import java.sql.*;
    import java.util.List;
    
    public class DBUtil {
        //1.定义需要的工具类对象
        protected Connection connection=null;
        protected PreparedStatement pps=null;
        protected ResultSet rs=null;
        protected int k=0;//受影响的行数
        private String url="jdbc:mysql://localhost:3306/flask";
        private String username="root";
        private String password="123456";
    
        //2.加载驱动
        static{
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        //3.获得连接
        protected Connection getConnection(){
            try {
                connection=DriverManager.getConnection(url,username,password);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
    
        //4.创建通道
        protected PreparedStatement getPps(String sql){
            try {
                getConnection();//insert into users values(?,?,?,?,)
                pps=connection.prepareStatement(sql);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return pps;
        }
    
        //5.给占位符赋值 list中保存的是给占位符所赋的值
        private void setParams(List list){
            try {
                if(list!=null&&list.size()>0){
                    for (int i=0;i<list.size();i++) {
                        pps.setObject(i+1,list.get(i));
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        //6.增删改调取的方法
        protected int update(String sql,List params){
            try {
                getPps(sql);
                setParams(params);
                k= pps.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return k;
        }
    
        //7.查询时调取
        protected ResultSet query(String sql, List list){
            try {
                getPps(sql);
                setParams(list);
                rs=pps.executeQuery();
                return rs;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        // 8. 关闭资源
        protected void closeall(){
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
  • 在dao层新增接口,并实现
    @Override
    public Student getByStuId(int id) {
        Student student = new Student();
        String sql = "select * from student where stuid=?";
        List list = new ArrayList();
        list.add(id);   // 预参数集合
        ResultSet result = query(sql, list);
        try {
            if (result.next()) {
                student.setStuid(result.getInt("stuid"));
                student.setStuname(result.getString("stuname"));
            }
            return student;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeAll();
        }
        return null;
    }
    
    // test.java
    public static void main(String[] args) {
        TeacherDao daot = new TeacherDaoImpl();
        Student student = daot.getByStuId(1);	// 单表查询
        System.out.println(student.getStuname());
    }
    
  • 一般在开发过程中,将固定在工具类中的字符串,放在属性文件
    • 名称必须是xxx.properties
    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/flask?serverTimezone=UTC
    user=root
    password=123456
    
    // 修改DBUtil
    private static String url;
    private static String username;
    private static String password;
    private static String driverName;
    
    //2.加载驱动
    static{
    	// 有两种方式
        try {
            InputStream inputStream = DBUtil.class.getClassLoader()
                    .getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            driverName = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("user");
            password = properties.getProperty("password");
            Class.forName(driverName);
    		
    		// 第二种方式
    	    ResourceBundle bundle = ResourceBundle.getBundle("db");
            driverName = bundle.getString("driver");
            url = bundle.getString("url");
            username = bundle.getString("user");
            password = bundle.getString("password");
            Class.forName(driverName);
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
    }
    

连接池

  • 将数据库连接作为对象存储在内存中,请求数据库时,从连接池中取出一个已建立的空闲连接对象
  • 自定义连接池
    • 需要定义指定的属性和方法
    • 属性: 集合,放置连接
    • 方法: 获取连接方法、回收连接方法
    public class Pool{
    	 static LinkedList<Connection> list = new LinkedList<Connection>();
    	 static{
    	   for (int i = 0; i < 10; i++) {
    	   // 在池子里初始化十个连接
    	     Connection connection = JDBCUtils.newInstance().getConnection();
    	     list.add(connection);
    	   }
    	 }
    	 /**
    	  * 从连接池子中获取连接的方式
    	  * @return
    	  */
    	 public static Connection getConnection(){
    	   
    	   if (list.isEmpty()) {
    	     //JDBCUtils类是自定义类,封装了连接数据库的信息代码
    	     Connection connection = JDBCUtils.newInstance().getConnection();
    	     list.addLast(connection);
    	   }
    	   
    	   Connection conn = list.removeFirst();
    	   
    	   return conn;
    	 }
    	 /**
    	  * 返回到连接池子中
    	  */
    	 public static void addBack(Connection conn){
    	   
    	   if (list.size() >= 10) {
    	     try {
    	       conn.close();
    	     } catch (SQLException e) {
    	       // TODO Auto-generated catch block
    	       e.printStackTrace();
    	     }
    	   }else{
    	     list.addLast(conn); //10
    	   }
    	   
    	 }
    	 
    	 
    	 /**
    	  * 获取连接池子中连接数量的方法
    	  */
    	 
    	 public static int getSize(){
    	   return list.size();
    	 }
    }
    
  • Java也定义了接口(规范),让我们实现连接池,具体实现代码如下
    • DataSource 接口有一个弊端,没有提供回收链接的方法,先装饰一下
    public class MyConnection implements Connection {
    	//将被装饰者导入
    	private Connection conn;
    	private LinkedList<Connection> list;
    	public MyConnection(Connection conn, LinkedList<Connection> list) {
    	  super();
    	  this.conn = conn;
    	  this.list = list;
    	}
    	@Override
    	public <T> T unwrap(Class<T> iface) throws SQLException {
    	  return conn.unwrap(iface);
    	}
    	@Override
    	public boolean isWrapperFor(Class<?> iface) throws SQLException {
    	  return conn.isWrapperFor(iface);
    	}
    	@Override
    	public Statement createStatement() throws SQLException {
    	  return conn.createStatement();
    	}
    	@Override
    	public PreparedStatement prepareStatement(String sql) throws SQLException {
    	  return conn.prepareStatement(sql);
    	}
    	@Override
    	public CallableStatement prepareCall(String sql) throws SQLException {
    	  return null;
    	}
    	@Override
    	public String nativeSQL(String sql) throws SQLException {
    	  return null;
    	}
    	@Override
    	public void setAutoCommit(boolean autoCommit) throws SQLException {    
    	}
    	@Override
    	public boolean getAutoCommit() throws SQLException {
    	  return false;
    	}
    	@Override
    	基于规范实现的连接池
    	public void commit() throws SQLException {
    	  conn.commit();
    	}
    	@Override
    	public void rollback() throws SQLException {
    	  conn.rollback();
    	}
    	@Override
    	public void close() throws SQLException {
    	  list.addLast(conn);
    	  
    	}
    	...
    }
    
    // 实现 DataSource 连接池
    public class DataSourcePool implements DataSource{
    	static LinkedList<Connection> list = new LinkedList<Connection>();
    	static{
    	  
    	  for (int i = 0; i < 10; i++) {
    	    Connection connection = JDBCUtils.newInstance().getConnection();
    	    list.add(connection);
    	  }
    	}
    	
    	public static int getSize(){
    	  return list.size();
    	}
    	@Override
    	public Connection getConnection() throws SQLException {
    	  Connection conn = list.removeFirst();
    	  MyConnection conn1 = new MyConnection(conn, list);
    	  return conn1;
    	}  
    	@Override
    	public PrintWriter getLogWriter() throws SQLException {
    	  return null;
    	}
    	@Override
    	public void setLogWriter(PrintWriter out) throws SQLException { 
    	}
    	@Override
    	public void setLoginTimeout(int seconds) throws SQLException { 
    	}
    	@Override
    	public int getLoginTimeout() throws SQLException {
    	  return 0;
    	}
    	@Override
    	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    	  return null;
    	}
    	@Override
    	public <T> T unwrap(Class<T> iface) throws SQLException {
    	  return null;
    	}
    	@Override
    	public boolean isWrapperFor(Class<?> iface) throws SQLException {
    	  return false;
    	}
    	@Override
    	public Connection getConnection(String username, String password) throws
    	SQLException {
    	  return null;
    	}
    }
    
    7
  • 也就是说,不需要我们写连接池的代码,用上面的某种就可以
  • 试一下DBCP的使用,将commons-dbcp-1.4.jar和commons-pool-1.5.6.jar导入进Modules
    // 重写DBUtils
    public class DBUtil {
        //1.定义需要的工具类对象
        protected Connection connection=null;
        protected PreparedStatement pps=null;
        protected ResultSet rs=null;
    
        protected int k=0;//受影响的行数
    
        private static String url;
        private static String username;
        private static String password;
        private static String driverName;
    
        private static BasicDataSource basicDataSource = new BasicDataSource();
    
        //2.加载驱动
        static{
            ResourceBundle bundle = ResourceBundle.getBundle("db");
            driverName = bundle.getString("driver");
            url = bundle.getString("url");
            username = bundle.getString("user");
            password = bundle.getString("password");
    
            basicDataSource.setUsername(username);
            basicDataSource.setPassword(password);
            basicDataSource.setUrl(url);
            basicDataSource.setDriverClassName(driverName);
            basicDataSource.setInitialSize(15);
        }
    
        //3.获得连接
        protected Connection getConnection(){
            try {
                connection=basicDataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
    
        //4.创建通道
        protected PreparedStatement getPps(String sql){
            try {
                getConnection();//insert into users values(?,?,?,?,)
                pps=connection.prepareStatement(sql);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return pps;
        }
    
        //5.给占位符赋值 list中保存的是给占位符所赋的值
        private void setParams(List list){
            try {
                if(list!=null&&list.size()>0){
                    for (int i=0;i<list.size();i++) {
                        pps.setObject(i+1,list.get(i)); // 从1开始
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        //6.增删改调取的方法
        protected int update(String sql,List params){
            try {
                getPps(sql);
                setParams(params);
                k= pps.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return k;
        }
    
        //7.查询时调取
        protected ResultSet query(String sql, List list){
            // 传入要执行的SQL和参数数组 (预通道)
            try {
                getPps(sql);
                setParams(list);
                rs=pps.executeQuery();
                return rs;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        // 8. 关闭资源
        protected void closeAll(){
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pps != null) {
                    pps.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
  • dbcp没有自动回收空闲连接的功能,c3p0有自动回收空闲连接功能
  • 在src下创建c3p0-config.xml,配置文件,名称固定;然后导入c3p0-0.9.1.2.jar
    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
        <default-config>
            <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/flask?serverTimezone=UTC</property>
            <property name="user">root</property>
            <property name="password">123456</property>
            <!-- 等待连接的超时时间,默认为0,代表无限等待,单位是毫秒 -->
            <property name="checkoutTimeout">30000</property>
            <!-- 检查空闲连接  默认为0 代表不检查 -->
            <property name="idleConnectionTestPeriod">30</property>
            <!-- 初始化连接 -->
            <property name="initialPoolSize">10</property>
            <!-- 最大空闲时间,超过这个时间的连接将被丢弃,默认为0,代表永远不关闭 -->
            <property name="maxIdleTime">50</property>
            <!-- 最大连接数 -->
            <property name="maxPoolSize">100</property>
            <!-- 最小连接数 -->
            <property name="minPoolSize">10</property>
            <!-- preparedStatement的缓存大小 -->
            <property name="maxStatements">200</property>
            <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
            <property name="AcquireIncrement">5</property>
        </default-config>
    </c3p0-config>
    
    • 你没看错,db.properties 都不需要了
    // 只需要改两行,不需要static静态代码块了
    ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
    //3.获得连接
    protected Connection getConnection(){
        try {
            connection = comboPooledDataSource.getConnection();
            // connection=basicDataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    
  • 目前,最流行的是阿里开发的德鲁伊,使用过程类似DBCP,需要db.properties
    // druid
    private static DruidDataSource druidDataSource = new DruidDataSource();
    

Web

  • 学习目标
    8
    9

Tomcat

  • 常见的开发模式:BS和CS
    • 浏览器——服务器:通过浏览器访问网站
    • 客户端——服务器:有客户端,不会实时更新代码
  • Tomcat是目前最流行的Java服务器
    • 服务器是部署在操作系统之上的,提供代码运行环境的容器
  • 下载与安装
    • 使用apache-tomcat-8.5.34,免安装,官网
    • 要熟悉解压后每个文件夹的作用
      11
      12
      13
    • 启动:双击bin/startup.bat,如果闪退,可能是8080端口占用;也可能需要修改此文件
      netstat -ano|findstr "8080"	# 找到PID
      tasklist|findstr "PID"
      
      # 或者在startup.首行加一句
      SET JAVA_HOME=D:\Java
      
  • 访问看是否启动:http://localhost:8080/
    • 使用shutdown.bat可以关闭
  • 在我们自己编写的项目目录`webapps``下
    • 新建文件夹,相当于创建一个项目
    • 新建HTML页面,测试访问
  • IDEA整合Tomcat
    • 可以大致参考:链接1链接2
      14
      15
    • 使用链接2,先直接创建普通Java项目
  • 点击启动,自动打开浏览器访问了:http://localhost:8080/demo_war_exploded/
    • 可以在tomcat配置文件中修改端口号
  • 路径太长,一般会在上图中的页面修改,设置Application context为 /
  • 默认打成war包,名称就是Artifact的名称,也是项目名称
    web工程: 
    	src: 存放java源代码 .java文件,下设不同package
    	web: 
    		web资源
    		WEB-INF(受服务器保护,浏览器不能直接访问)
    			lib-存放第三方jar包
    			web.xml 为整个web工程的配置部署描述文件,servlet listener filter session等配置信息
    		index.jsp 在浏览器中输入工程名时,默认访问的资源
    
  • 增加管理员信息:conf/tomcat-users.xml,感觉没啥用
    <role rolename="manager-gui"/>
    <user username="tomcat" password="tomcat" roles="manager-gui"/>
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Roy_Allen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值