已有的知识-jdbc(康师傅版)
先要知道tomcat的部署和配置
tomcat对外就是web服务器,对内是Servlet容器,在idea中关联上tomcat的话,就可以在idea中启动tomcat,然后访问我们的web项目了,而且通过start.bat打开和idea打开是不能同时存在的,开启一个的时候应该把前一个关闭
数据库就是mysql
Thymeleaf的相关知识
目录
各种配置------
开始看
先看一下整体的文件及配置情况
看一下具体的
一
二
三
一开始觉得实在是太繁琐了,总会有各种各样的错误,然后也不知道jar包应该放在什么地方更好,慢慢的跟着做的多次了,之后才有点感觉,在排除了那么多错误之后,才会对这些问题熟悉起来,jar包的话,可以在每个module里面自己建一个lib文件夹,然后放到里面再右键选择add...,也可以放到最下面那个地方,可以给全局使用,用到的时候引入就好了,这其中这这那那的需要注意的地方太多,只能通过在自己的不断的试错去解决,而且很多其实是细枝末节的地方,比如有的路径少写个0什么的。。。
这是module里的所有文件
右边的是lib里的jar包,里面除了连接数据库的驱动都是thymeleaf需要的,tomcat在其他地方引入的
上面这个图就是引入情况
目前各方面更深入的情况还不是很了解
想要沟通一个数据库的表,先来处理这一部分
创建表的类Fruit.class
package com.xue.fruit.pojo;
/**
* @author xxx
* @create 2022-04-16 22:10
*/
public class Fruit {
private Integer fid ;
private String fname ;
private Integer price ;
private Integer fcount ;
private String remark ;
public Fruit(){}
public Fruit(Integer fid, String fname, Integer price, Integer fcount, String remark) {
this.fid = fid;
this.fname = fname;
this.price = price;
this.fcount = fcount;
this.remark = remark;
}
public Integer getFid() {
return fid;
}
public void setFid(Integer fid) {
this.fid = fid;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Integer getFcount() {
return fcount;
}
public void setFcount(Integer fcount) {
this.fcount = fcount;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return fid + "\t\t" + fname + "\t\t" + price +"\t\t" + fcount +"\t\t" + remark ;
}
}
再创建一个接口 包含我们需要对表做的所有基本操作,其中的残数等等,在后面都是用得到的
package com.xue.fruit.dao;
import com.xue.fruit.pojo.Fruit;
import java.util.List;
/**
* @author xxx
* @create 2022-04-16 22:20
*/
public interface FruitDAO {
//获取指定页码上的库存列表信息 每页显示5条
List getFruitList(String keyword,Integer pageNo);
//工具fid获取特定的水果库存信息
Fruit getFruitByFid(Integer fid);
//修改指定的库存记录
void updateFruit(Fruit fruit);
//根据fid删除指定的库存记录
void delFruit(Integer fid);
//添加新库存记录
void addFruit(Fruit fruit);
//查询库存总记录条数
int getFruitCount(String keyword);
}
我们想要对数据库进行操作,当然还需要获取连接等等的一系列操作,如下持久化的DAO层
package com.xue.myssm.basedao;
/**
*
*
* 配置DAO文件
* 配置信息也在其中
* 这个方法应该不咋好用
* @author xxx
* @create 2022-04-15 19:42
*/
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseDAO {
public final String DRIVER = "com.mysql.cj.jdbc.Driver" ;
public final String URL = "jdbc:mysql://localhost:3306/fruitdb?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&db_daily_management&serverTimezone=GMT&useSSL=false";
public final String USER = "root";
public final String PWD = "xzh520" ;
protected Connection conn ;
protected PreparedStatement psmt ;
protected ResultSet rs ;
//T的Class对象
private Class entityClass ;
public BaseDAO(){
//getClass() 获取Class对象,当前我们执行的是new FruitDAOImpl() , 创建的是FruitDAOImpl的实例
//那么子类构造方法内部首先会调用父类(BaseDAO)的无参构造方法
//因此此处的getClass()会被执行,但是getClass获取的是FruitDAOImpl的Class
//所以getGenericSuperclass()获取到的是BaseDAO的Class
Type genericType = getClass().getGenericSuperclass();
//ParameterizedType 参数化类型
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
//获取到的中的T的真实的类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
protected Connection getConn(){
try {
//1.加载驱动
Class.forName(DRIVER);
//2.通过驱动管理器获取连接对象
return DriverManager.getConnection(URL, USER, PWD);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null ;
}
protected void close(ResultSet rs , PreparedStatement psmt , Connection conn){
try {
if (rs != null) {
rs.close();
}
if(psmt!=null){
psmt.close();
}
if(conn!=null && !conn.isClosed()){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//给预处理命令对象设置参数
private void setParams(PreparedStatement psmt , Object... params) throws SQLException {
if(params!=null && params.length>0){
for (int i = 0; i < params.length; i++) {
psmt.setObject(i+1,params[i]);
}
}
}
//执行更新,返回影响行数
protected int executeUpdate(String sql , Object... params){
boolean insertFlag = false ;
insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
try {
conn = getConn();
if(insertFlag){
psmt = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
}else {
psmt = conn.prepareStatement(sql);
}
setParams(psmt,params);
int count = psmt.executeUpdate() ;
if(insertFlag){
rs = psmt.getGeneratedKeys();
if(rs.next()){
return ((Long)rs.getLong(1)).intValue();
}
}
return count ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
close(rs,psmt,conn);
}
return 0;
}
//通过反射技术给obj对象的property属性赋propertyValue值
private void setValue(Object obj , String property , Object propertyValue){
Class clazz = obj.getClass();
try {
//获取property这个字符串对应的属性名 , 比如 "fid" 去找 obj对象中的 fid 属性
Field field = clazz.getDeclaredField(property);
if(field!=null){
field.setAccessible(true);
field.set(obj,propertyValue);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
//执行复杂查询,返回例如统计结果
protected Object[] executeComplexQuery(String sql , Object... params){
try {
conn = getConn() ;
psmt = conn.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
Object[] columnValueArr = new Object[columnCount];
//6.解析rs
if(rs.next()){
for(int i = 0 ; i executeQuery(String sql , Object... params){
List list = new ArrayList<>();
try {
conn = getConn() ;
psmt = conn.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集数据的数据 , 简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
//获取结果集的列数
int columnCount = rsmd.getColumnCount();
//6.解析rs
while(rs.next()){
T entity = (T)entityClass.newInstance();
for(int i = 0 ; i
把接口还有对数据库的普通通用操作写好之后,就可以编写我们需要的功能
package com.xue.fruit.dao.impl;
import com.xue.fruit.dao.FruitDAO;
import com.xue.fruit.pojo.Fruit;
import com.xue.myssm.basedao.BaseDAO;
import java.util.List;
/**
* @author xxx
* @create 2022-04-16 22:21
*/
public class FruitImpl extends BaseDAO implements FruitDAO{
@Override
public List getFruitList(String keyword,Integer pageNo) {
return super.executeQuery("select * from t_fruit where fname like ? or remark like ? limit ? , 5","%"+keyword+"%","%"+keyword+"%",(pageNo-1)*5);
}
@Override
public Fruit getFruitByFid(Integer fid) {
return super.load("select * from t_fruit where fid = ?",fid);
}
@Override
public void updateFruit(Fruit fruit) {
String sql = "update t_fruit set fname = ?,price = ?,fcount = ?,remark = ? where fid = ?";
super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
}
@Override
public void delFruit(Integer fid) {
super.executeUpdate("delete from t_fruit where fid = ?",fid);
}
@Override
public void addFruit(Fruit fruit) {
String sql = "insert into t_fruit values(0,?,?,?,?)";
super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark());
}
@Override
public int getFruitCount(String keyword) {
//这个地方的转型 也很重要
return ((Long) super.executeComplexQuery("select count(*) from t_fruit where fname like ? or remark like ?","%"+keyword+"%","%"+keyword+"%")[0]).intValue();
}
}
对数据库的一部分梳理上面就完成了。
现在我们需要完成我们设想的web项目,那么就开始一步一步的增加功能,这里都是最终版的代码
先写一个首页index.html
Title
有了前端的页面之后想要沟通服务器与数据库 自然有Servlet,这里面有很多是一开始没有添加的代码
package com.xue.fruit.servlets;
import com.xue.fruit.dao.FruitDAO;
import com.xue.fruit.dao.impl.FruitImpl;
import com.xue.fruit.pojo.Fruit;
import com.xue.myssm.myspringmvc.ViewBaseServlet;
import com.xue.myssm.util.StringUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @author xxx
* @create 2022-04-16 22:25
*/
//Servlet从3.0开始支持注解方式的注册 其实就把不同url对应的资源注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
//请求是从浏览器发到我们的程序 响应是从我们的程序发给浏览器
//Servlet等于说就是服务器端的小程序
//Servlet主要负责处理请求、协调调度功能
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("UTF-8");
//获取当前的会话session-----作用域问题
HttpSession session = req.getSession();
//设置当前页 默认值为1
Integer pageNo = 1;
//获取form表单中oper的值
String oper = req.getParameter("oper");
//如果oper!=null 说明 通过表单的查询按钮点击过来的
//如果oper是空的,说明 不是通过表单的查询按钮点击过来的
String keyword = null;//关键字 就查询的那个字
if(StringUtil.isNotEmpty(oper)&&"search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1,keyword应该从请求参数中获取
pageNo = 1;//重新设置为1 新表的第一页
keyword = req.getParameter("keyword");//从请求中获取关键字
if(StringUtil.isEmpty(keyword)){//这个时候是点了查询但是其实没有写任何东西
keyword = "";//所以再设为空 让他不null 不然的话查询的时候会拼接是%null%
}
session.setAttribute("keyword",keyword);//为session加入keyword
}else{
//说明此处不是点击表单查询发送过来的请求(比如上面的上一页下一页或者地址栏输入网址)
//此时keyword应该从session作用域获取
String pageNoStr=req.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
//如果请求中读得到得时候就强转 如果读不到就有上面得1
}
//如果不是点击的查询按钮 那么查询是基于session中的保存的现有的keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj;
}else{
keyword = "";//比如第一次进入网页的时候
}
}
//反正已经分为了有内容得查询和无内容得查询
//在作用域 重新更新当前页得值
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitImpl();
//获取库存列表
List fruitList = fruitDAO.getFruitList(keyword,pageNo);
//保存到session中
session.setAttribute("fruitList",fruitList);
//获取总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总条数 总页数
//fruitCount (fruitCount+5-1)/5
int pageCount = (fruitCount+5-1)/5;
session.setAttribute("pageCount",pageCount);
//此处视图的名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称上去
//物理视图=视图前缀+逻辑视图+视图后缀
//逻辑视图名称 index
//物理视图名称 view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称就是 / index .html
super.processTemplate("index",req,resp);
}
}
还有增加列的界面,在次界面修改后,通过post方式提交到服务器,修改数据库的信息
Title
新增库存信息3
跳转到add.do的服务器小程序,配置信息都可以写道xml但是这里没有写
package com.xue.fruit.servlets;
import com.xue.fruit.dao.FruitDAO;
import com.xue.fruit.dao.impl.FruitImpl;
import com.xue.fruit.pojo.Fruit;
import com.xue.myssm.myspringmvc.ViewBaseServlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author xxx
* @create 2022-04-18 15:07
*/
@WebServlet("/add.do")
public class AddServlet extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("UTF-8");
//获取数据
String fname = req.getParameter("fname");
Integer price = Integer.parseInt(req.getParameter("price"));
Integer fcount = Integer.parseInt(req.getParameter("fcount"));
String remark = req.getParameter("remark");
Fruit fruit = new Fruit(0,fname,price,fcount,remark);
fruitDAO.addFruit(fruit);
resp.sendRedirect("index");
}
}
还有修改信息的界面
Title
编辑库存信息
然后转到updateServlet
package com.xue.fruit.servlets;
import com.xue.fruit.dao.FruitDAO;
import com.xue.fruit.dao.impl.FruitImpl;
import com.xue.fruit.pojo.Fruit;
import com.xue.myssm.myspringmvc.ViewBaseServlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author xxx
* @create 2022-04-17 17:50
*/
@WebServlet("/update.do")
public class UpdateSevlet extends ViewBaseServlet{
private FruitDAO fruitDAO = new FruitImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding("utf-8");
//2.获取参数
String fidStr = req.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
Integer fcount=Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
//3.执行更新
fruitDAO.updateFruit(new Fruit(fid,fname,price,fcount,remark));
//4.资源跳转
//super.processTemplate("index",req,resp);
//req.getRequestDispatcher("index.html").forward(req,resp);
//此处需要重定向 目的是重新给IndexServlet发请求,重新获取fruitList,然后覆盖到session中,这样index.html页面显示的session中的数据才是最新的
resp.sendRedirect("index");
}
}
删除的
package com.xue.fruit.servlets;
import com.xue.fruit.dao.FruitDAO;
import com.xue.fruit.dao.impl.FruitImpl;
import com.xue.myssm.myspringmvc.ViewBaseServlet;
import com.xue.myssm.util.StringUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author xxx
* @create 2022-04-18 12:22
*/
@WebServlet("/del.do")
public class DelServlet extends ViewBaseServlet{
private FruitDAO fruitDAO = new FruitImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fidStr=req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
resp.sendRedirect("index");
}
}
}
基本的功能就完成
还需要一些配置类的文件
css
add.css
*{
color: threeddarkshadow;
}
a{
text-decoration: none;
}
body{
margin:0;
padding:0;
background-color:#808080;
}
div{
position:relative;
float:left;
}
#div_container{
width:80%;
height:100%;
border:0px solid blue;
margin-left:10%;
float:left;
background-color: honeydew;
border-radius:8px;
}
#div_fruit_list{
width:100%;
border:0px solid red;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:16px;
margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.delImg{
width:24px;
height:24px;
}
.btn{
border:1px solid lightgray;
width:80px;
height:24px;
}
.center{
text-align:center;
}
.f30{
font-size: 30px;
}
edit.css
*{
color: threeddarkshadow;
}
a{
text-decoration: none;
}
body{
margin:0;
padding:0;
background-color:#808080;
}
div{
position:relative;
float:left;
}
#div_container{
width:80%;
height:100%;
border:0px solid blue;
margin-left:10%;
float:left;
background-color: honeydew;
border-radius:8px;
}
#div_fruit_list{
width:100%;
border:0px solid red;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:16px;
margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.delImg{
width:24px;
height:24px;
}
.btn{
border:1px solid lightgray;
width:80px;
height:24px;
}
.center{
text-align:center;
}
.f30{
font-size: 30px;
}
index.css
*{
color: threeddarkshadow;
}
a{
text-decoration: none;
}
body{
margin:0;
padding:0;
background-color:#808080;
}
div{
position:relative;
float:left;
}
#div_container{
width:80%;
height:100%;
border:0px solid blue;
margin-left:10%;
float:left;
background-color: honeydew;
border-radius:8px;
}
#div_fruit_list{
width:100%;
border:0px solid red;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:16px;
margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.delImg{
width:24px;
height:24px;
}
.btn{
border:1px solid lightgray;
width:80px;
height:24px;
}
.center{
text-align:center;
}
.f30{
font-size: 30px;
}
index.js
function delFruit(fid) {
if(confirm('是否确认删除')){
window.location.href='del.do?fid='+fid;
}
}
function page(pageNo) {
window.location.href="index?pageNo="+pageNo;
}
还有xml里对thymeleaf视图的配置
view-prefix
/
view-suffix
.html
视图的类
package com.xue.myssm.myspringmvc;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
//处理模板
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
最后看看这简陋的成品
虽然不堪入目,但是人是会进步的