文章目录
- 数据库
-
- 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代码中要多加个集合属性
- 接下来创建对应的类和属性,显然,这些类都是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层,定义一对多的查询接口和实现类(一个老师对应多个学生)
// 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(背)
- 一般设置手动提交
// 获取连接时 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;} }
- 也就是说,不需要我们写连接池的代码,用上面的某种就可以
- 试一下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
- 学习目标
Tomcat
- 常见的开发模式:BS和CS
- 浏览器——服务器:通过浏览器访问网站
- 客户端——服务器:有客户端,不会实时更新代码
- Tomcat是目前最流行的Java服务器
- 服务器是部署在操作系统之上的,提供代码运行环境的容器
- 下载与安装
- 使用apache-tomcat-8.5.34,免安装,官网
- 要熟悉解压后每个文件夹的作用
- 启动:双击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
- 使用链接2,先直接创建普通Java项目
- 可以大致参考:链接1,链接2
- 点击启动,自动打开浏览器访问了: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"/>