当前位置: 代码迷 >> 综合 >> 新职课(chapter5-1)
  详细解决方案

新职课(chapter5-1)

热度:33   发布时间:2023-12-20 12:58:23.0

文章目录

  • 数据库
    • JDBC
    • SQL注入
    • 两表关系
    • 事务
    • 批处理
    • 工具类
    • 连接池
  • Web
    • Tomcat

数据库

  • 基础的原理就不讲了,直接看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. 定义SQLstat = 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 {
          @Overridepublic 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模型的MTeacherDao daot = new TeacherDaoImpl(); // Impl是具体实现,类似List和ArrayListTeacher 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 blocke.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;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {
          return conn.unwrap(iface);}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {
          return conn.isWrapperFor(iface);}@Overridepublic Statement createStatement() throws SQLException {
          return conn.createStatement();}@Overridepublic PreparedStatement prepareStatement(String sql) throws SQLException {
          return conn.prepareStatement(sql);}@Overridepublic CallableStatement prepareCall(String sql) throws SQLException {
          return null;}@Overridepublic String nativeSQL(String sql) throws SQLException {
          return null;}@Overridepublic void setAutoCommit(boolean autoCommit) throws SQLException {
              }@Overridepublic boolean getAutoCommit() throws SQLException {
          return false;}@Override基于规范实现的连接池public void commit() throws SQLException {
          conn.commit();}@Overridepublic void rollback() throws SQLException {
          conn.rollback();}@Overridepublic 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();}@Overridepublic Connection getConnection() throws SQLException {
          Connection conn = list.removeFirst();MyConnection conn1 = new MyConnection(conn, list);return conn1;}  @Overridepublic PrintWriter getLogWriter() throws SQLException {
          return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {
           }@Overridepublic void setLoginTimeout(int seconds) throws SQLException {
           }@Overridepublic int getLoginTimeout() throws SQLException {
          return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {
          return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {
          return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {
          return false;}@Overridepublic Connection getConnection(String username, String password) throwsSQLException {
          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文件,下设不同packageweb: 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"/>