JavaWeb
前言
前情回顾:【JavaWeb】-Tomcat、Eclipse使用项目搭建(一):https://blog.youkuaiyun.com/hello_list/article/details/124343254
如果你直接看大这篇文章还没进行环境搭建,可以回到第一篇搭建下环境,这样就可以完成这一部分我们对代码的操作;
需要得基础:会sql语句,基本的增删改查;
这里MySql没有说,后面有机会给大家出一个完整的mysql的知识点;看到这里希望可以给博主点个赞哦!
mysql
官网:https://www.mysql.com/
不过多介绍了,看下百度百科的介绍吧:
下载安装
我们使用mysql8
选择安装包:
这里就不带着大家安装了,网上教程很多,安装一个软件嘛,不过这里还是看着别人的图文教程一步步走比较好,现在马上去搜索一个安装教程,安装完后马上回来哦!
再说一下,万事开头配置难,配好一个环境对开发很重要,并且在配置环境的时候可能会出各种错,如果解决不了,大家换mysql版本,多重装几次,一定可以的
navicat
navicat是用来管理数据的一个可视化工具:我们不想用命令行一直在操作mysql,所以我们使用一个可视化工具更方便管理数据库;
当然这个软件是收费的,我们可以破解一下,这里大家也可以使用别的可视化工具,sqlyog都可以;
这里还是不带着大家安装了,教程很多,很简单大家快去安装上,这里给大家方便,随便找了一个教程,还是那句话,都不难很简单,快去安装完回来:http://t.zoukankan.com/yudx-p-13522514.html
JDBC
你回来了,那么我们就正式进入JDBC的学习了,什么是JDBC,来自百度百科:
其实就是java操作数据库的规范,我们知道关系型数据库有很,比如sqlserver 、mysql等等,但是不同的数据库我们不能去学很多种操作吧,这个时候为方便就可以规定了jdbc协议,我们使用不同的数据库,只需要切换数据源就可以,操作规范都是一样的,当然就省事了;
那对于Jdbc我们学习这么几个,连接数据库,操作数据库,断开数据库,直接上代码,连接数据库:
环境准备
在使用jdbc之前我们首先引入mysql驱动依赖:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
数据库表,我这里创建了一张货物表(Good):
/*
Navicat MySQL Data Transfer
Source Server : 本机
Source Server Version : 80011
Source Host : localhost:3306
Source Database : huanying
Target Server Type : MYSQL
Target Server Version : 80011
File Encoding : 65001
Date: 2022-04-20 17:36:09
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for good
-- ----------------------------
DROP TABLE IF EXISTS `good`;
CREATE TABLE `good` (
`goodid` int(20) NOT NULL,
`goodname` varchar(20) NOT NULL,
`surplus` int(20) NOT NULL,
`goodprice` int(20) NOT NULL,
PRIMARY KEY (`goodid`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of good
-- ----------------------------
INSERT INTO `good` VALUES ('1', '黑色森林', '2', '15');
INSERT INTO `good` VALUES ('2', '奶油蛋糕', '3', '13');
INSERT INTO `good` VALUES ('3', '水果蛋糕', '2', '11');
INSERT INTO `good` VALUES ('4', '冰淇凌蛋糕', '5', '13');
INSERT INTO `good` VALUES ('9', '牛奶蛋糕', '34', '9');
INSERT INTO `good` VALUES ('11', '肉松蛋糕', '13', '13');
查询操作
package dao;
import java.sql.*;
public class TesterDao {
public static void main(String[] args) {
// 四大属性
String DRIVER = "com.mysql.jdbc.Driver";
String URL = "jdbc:mysql://localhost:3306/huanying?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8";
String NAME = "root";
String PASSWORD = "root";
// 三大对象
Connection con = null; // 连接对象
PreparedStatement ps = null; // sql语句编译对象
ResultSet rs = null; // 返回结果集,查询对象
// 连接mysql
try {
Class.forName(DRIVER);
con = DriverManager.getConnection(URL, NAME, PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// 编译sqly语句,
String sql = "select * from good";
try {
//con.createStatement() 这个不安全,不使用
// 从数据库连接中获取sql语句
ps = con.prepareStatement(sql);
// 执行sql,并返回结果集,查询使用executeQuery,增删改使用executeUpdate
rs = ps.executeQuery();
// 便利取出结果集内容
while (rs.next()) {
System.out.println(rs.getInt(1)); // 从第一列返回
System.out.println(rs.getString(2)); // 从第二列返回
System.out.println(rs.getInt(3)); // 从第三列返回
System.out.println(rs.getInt(4)); // 从第四列返回
}
} catch (Exception e) {
e.printStackTrace();
}
// 最后关闭连接,从123=》321这个顺序关闭
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
}
}
}
结果
增删改操作
修改我们只需要改变一个地方就可以,1、连接数据库,2、执行sql,返回结果,3、关闭连接,这里我们主要做第三步
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test02Dao {
public static void main(String[] args) {
// 四大属性
String DRIVER = "com.mysql.jdbc.Driver";
String URL = "jdbc:mysql://localhost:3306/huanying?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8";
String NAME = "root";
String PASSWORD = "root";
// 三大对象
Connection con = null; // 连接对象
PreparedStatement ps = null; // sql语句编译对象
ResultSet rs = null; // 返回结果集,查询对象
// 连接mysql
try {
Class.forName(DRIVER);
con = DriverManager.getConnection(URL, NAME, PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// 编译sql语句,添加一条数据
String sql = "insert into good values(12,'草莓蛋糕',22,23)";
try {
// 从数据库连接中获取sql语句
ps = con.prepareStatement(sql);
// 执行sql,并返回结果集,查询使用executeQuery,增删改使用executeUpdate
//增删改只返回影响数据库行数,int类型
//删改都一样,尝试下
int result = ps.executeUpdate();
if (result==1) {
System.out.println("添加数据成功");
}
} catch (Exception e) {
e.printStackTrace();
}
// 最后关闭连接,从123=》321这个顺序关闭
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
}
}
}
运行结果
sql预编译
那我们知道,这样写的sql语句是死的,但是如果靠字符串拼接,那就很不安全,这里我们可以实现sql预编译,通过?去充当占位符那样,这里我们使用修改尝试下
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Testr03Dao {
public static void main(String[] args) {
// 四大属性
String DRIVER = "com.mysql.jdbc.Driver";
String URL = "jdbc:mysql://localhost:3306/huanying?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8";
String NAME = "root";
String PASSWORD = "root";
// 三大对象
Connection con = null; // 连接对象
PreparedStatement ps = null; // sql语句编译对象
ResultSet rs = null; // 返回结果集,查询对象
// 连接mysql
try {
Class.forName(DRIVER);
con = DriverManager.getConnection(URL, NAME, PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// 编译sql语句,添加一条数据
String sql = "update good set surplus=? where goodid=?";
//两个从前台获取的值
int surplus=99;
int goodid=12;
try {
// 从数据库连接中获取sql语句
ps = con.prepareStatement(sql);
//补充完sql
ps.setObject(1, surplus); //点一下,有很多set类型的方法object更好用,两个参数:从左开始第几个?,填充什么值
ps.setObject(2, goodid);
// 执行sql,并返回结果集,查询使用executeQuery,增删改使用executeUpdate
//增删改只返回影响数据库行数,int类型
int result = ps.executeUpdate();
if (result==1) {
System.out.println("修改数据成功");
}
} catch (Exception e) {
e.printStackTrace();
}
// 最后关闭连接,从123=》321这个顺序关闭
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
}
}
}
运行结果
DBCP
介绍
来自百度百科:
dbcp大家不要多想就是一个池化概念,什么操作都没有,就是在jdbc的基础上多了些配置;
池化技术,百度百科介绍的很清楚,这里我在给大家比喻一下,好比你从一个房间进出,你需要不停的开关门,进出很多次是不是很烦,麻烦对不对,这个时候如果我们一直把门打开这,这样进出不就很方便,门卫,对吧,他会自己去管理这门什么时候关什么时候一直开着,所以就很方便,不理解没关系,不妨碍我们使用;
环境改变
想要使用dbcp需要导入dbcp的三个jar包:
commons-pool2-2.4.2.jar,commons-logging-1.2.jar,commons-dbcp2-2.1.1.jar
maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>${dbcp.version}</version>
</dependency>
创建一个配置文件,db.properties文件用来放置配置文件,这个是在网上找的比较全点
#全部的参数参考,org.apache.commons.dbcp2.BasicDataSourceFactory里的ALL_PROPERTIES
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/huanying?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
username=root
password=
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk;serverTimezone=UTC
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
创建DBCPUtil工具类:
package dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
// 封装Dbcp连接池的工具类
// 该类中提供了获取连接和关闭连接的通用方法
public class DbcpUtil {
// 使用ThreadLocal来解决线程同步问题.让每个线程都使用各自的连接对象
// ThreadLocal<Connection> 可以被其中的Connection做成多份拷贝,让不同的线程对象使用
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
private static DataSource ds;
// 静态代码块会在程序执行之前,被自动的执行
static {
InputStream in = DbcpUtil.class.getResourceAsStream("db.properties");
Properties pro = new Properties();
try {
pro.load(in);
ds = BasicDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接的时候,把方法中遇到的异常通过throws抛出去,抛给调用者
// 通过异常在方法和方法之间传递信息
public static Connection getConnectio() throws SQLException {
Connection conn = threadLocal.get();
// 如果conn等于null 或者conn已经关闭
if (conn == null || conn.isClosed()) {
conn = ds.getConnection();
threadLocal.set(conn);
}
return conn;
}
public static void closeConnection() {
Connection conn = threadLocal.get();
try {
// 如果conn不等于null 并且没有关闭
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
threadLocal.set(null);
}
}
}
查询数据库
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBCPTester01 {
public static void main(String[] args) {
// 三大对象
Connection con = null; // 连接对象
PreparedStatement ps = null; // sql语句编译对象
ResultSet rs = null; // 返回结果集,查询对象
try {
// 1、获取连接对象,改变了
con = DbcpUtil.getConnectio();
// 2、执行sql语句
// 编译sql语句,
String sql = "select * from good";
// 从数据库连接中获取sql语句
ps = con.prepareStatement(sql);
// 执行sql,并返回结果集,查询使用executeQuery,增删改使用executeUpdate
rs = ps.executeQuery();
// 便利取出结果集内容
while (rs.next()) {
System.out.println(rs.getInt(1)); // 从第一列返回
System.out.println(rs.getString(2)); // 从第二列返回
System.out.println(rs.getInt(3)); // 从第三列返回
System.out.println(rs.getInt(4)); // 从第四列返回
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//最后关闭连接,这里的关闭不是像jdbc直接关闭了连接,而是放回来连接池
DbcpUtil.closeConnection();
}
}
}
运行结果
我们发现知识配置一下,知识获得连接和关闭连接的方式变了,其他都没变,很简单吧,主要就是理解其中的概念;
BaseDao
我们发现是不是操作数据库总共分三步:1、连接mysql 2、执行sql语句 3、关闭数据库,我们发现其实只有第二步是我们一直使用在改变的,第一步和第三步,完全没有必要每次写那么多代码,我们都是学过面向对象的人了,那我们就可以对这个进行下简单的封装;
对于增删改方法,我们发现PreparedStatement对象都是执行的executeUpdate方法,那就简单了,增删改我们封装到一个方法里:
public int executeUpdate(String sql, Object... param) {
int result = 0;
try {
// 通过DBCP获取连接
con = DbcpUtil.getConnectio();
ps = con.prepareStatement(sql);
for (int i = 0; i < param.length; i++) {
ps.setObject(i, param[i]);
}
result = ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DbcpUtil.closeConnection();
}
return result;
}
但是对于查询方法的封装,对于每张表都不一样,通过sql查询的结果也都不一样,所以我们封装到sql执行把ResultSet结果集对象交出给自定义处理:
public ResultSet executeQuery(String sql, Object... param) {
try {
con = DbcpUtil.getConnectio();
ps = con.prepareStatement(sql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
ps.setObject(i++, param[i]);
}
}
rs = ps.executeQuery();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rs;
}
为什么这里我没有关闭连接呢,你尝试下,是不是就报错了,因为数据都在ResultSet对象里,如果我们关闭连接就是ResultSet关闭,当然会报错,并且数据取不出来;
最后完整的BaseDao,我这里只是做了下简单的封装,并且可能也封装的不好,大家根据自己的业务,到时候会有更好的封装,方便使用:
package Dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class BaseDao {
// 三大对象
Connection con = null; // 连接对象
PreparedStatement ps = null; // sql语句编译对象
ResultSet rs = null; // 返回结果集,查询对象
public int executeUpdate(String sql, Object... param) {
int result = 0;
try {
// 通过DBCP获取连接
con = DbcpUtil.getConnectio();
ps = con.prepareStatement(sql);
for (int i = 0; i < param.length; i++) {
ps.setObject(i+1, param[i]);
}
result = ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DbcpUtil.closeConnection();
}
return result;
}
public ResultSet executeQuery(String sql, Object... param) {
try {
con = DbcpUtil.getConnectio();
ps = con.prepareStatement(sql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
ps.setObject(i++, param[i]);
}
}
rs = ps.executeQuery();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rs;
}
}
测试:
package Dao;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class Test01Dao extends BaseDao {
// 修改一个good的名字
public int updateTest(String sql, String goodName, int goodId) {
return this.executeUpdate(sql, goodName, goodId);
}
// 查询all
public void queryTest(String sql) {
this.executeQuery(sql);
try {
while (rs.next()) {
System.out.println(rs.getInt(1)); // 从第一列返回
System.out.println(rs.getString(2)); // 从第二列返回
System.out.println(rs.getInt(3)); // 从第三列返回
System.out.println(rs.getInt(4)); // 从第四列返回
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
DbcpUtil.closeConnection();
}
}
public static void main(String[] args) {
Test01Dao test01Dao = new Test01Dao();
// test01Dao.queryTest("select * from good");
int res = test01Dao.updateTest("update good set goodname=? where goodid=?", "hello", 17);
if (res == 1) {
System.out.println("修改成功");
}
}
}
查询全部结果,测试成功:
执行修改,这里只是执行了修改,删除和增加还用演示吗,不用了吧:
结果,测试成功:
小结
那看到这里我们发现这些都不是很难,反而很简单有没有,而且特别方便,在面对实际业务开发的时候在这层代码上进行封装和继承,可以更大的简化我们的代码开发;
那看到这里了,是不是可以关注下博主,希望这篇文章对你有帮助,后面还会有很多的,同时也谢谢大家观看
下期预告:mvc、servlet;