day11 今日内容
0 复习昨日 1 JDBC概述 2 JDBC开发步骤 3 完成增删改操作 4 ResultSet 5 登录案例
0 复习昨日
1 写出JQuery,通过获得id获得dom,并给input输入框赋值的语句 $(“#id”).val(“值”)
2 mysql内连接和外连接的区别 内连接只会保留完全符合关联条件的数据 外连接会保留表(左外保留左表)中不符合关联条件的数据
3 事务是什么? 事务是逻辑一组操作,要么全部成功,要么全部失败
4 索引有什么好处和坏处 索引可以提高查询效率
如果表是经常性的需要增删改,有索引在就会非常慢
1 JDBC概述
目前我们操作数据库,只能通过命令行(cmd)或者图形工具Navicat来操作数据库.
但是实际开发时配合页面数据对数据操作,如果还是使用命令行(cmd)或者图形工具Navicat来操作就很麻烦!
JDBC就是另外一种操作数据库的方式.(Java操作数据库)
JDBC: Java DataBase Connectivity Java 数据库连接
JDBC的设计思想
Mysql厂商提供了驱动包,如下(jar包)(jar包就是把java项目压缩打包)
驱动包,就是MYSQL厂商提供一套JDBC规范的实现.
每个知识点小问?
1 2 3 4 什么是JDBC ? 设计思想 ? 什么是jar包 ? mysql驱动包是什么 ?
2 JDBC开发步骤 2.1 创建java项目 2.2 导入mysql驱动包 mysql厂商提供的jdbc规范的实现,要想完成JDBC操作,就需要将驱动包加入到当前项目中.
2.2.1 复制粘贴版本
将mysql驱动包复制粘贴到此处
添加驱动包为当前项目的类库
2.2.2 idea导入类库版本
打开项目结构(Project Structure)
选择libraries,添加jar包
应用生效
成功
2.3 JDBC编程 准备数据库表,进行CRUD.
1 2 3 4 5 6 7 8 9 create table tb_user( id int (11 ) primary key auto_increment comment '用户编号' , username varchar (10 ) comment '用户名' , password varchar (10 ) comment '密码' , phone varchar (11 ) comment '手机号' , createTime date comment '注册时间' , money double (10 ,2 ) comment '账户余额' , sex int (1 ) comment '性别 1男2女' );
需求: 使用JDBC完成对tb_user表插入数据
JDBC编程有标准步骤(八股文)
注册驱动
连接数据库
获得执行SQL的对象
执行SQL语句,获得结果
关流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.qf.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class Demo1_insert { public static void main(String[] args) throws ClassNotFoundException, SQLException { / / 1 加载驱动 / / ps: 利用反射技术,将驱动类加载到JVM Class.forName("com.mysql.jdbc.Driver"); / / 2 通过驱动管理对象获得连接对象 String url = "jdbc:mysql://localhost:3306/java2217?useSSL=false&serverTimezone=UTC"; String username = "root"; String password = "123456"; Connection conn = DriverManager.getConnection(url,username,password); / / 3 通过连接对象,创建执行sql 语句的对象 Statement statement = conn.createStatement(); / / 4 通过执行语句对象,执行sql ,获得结果 String sql = "insert into tb_user (id,username,password,phone,createTime,money,sex) values (2,'root','123456','1122200','2022-11-21',2000.0,2)"; / / 执行查询,是executeQuery() / / 执行增删改,是executeUpdate(),返回受影响的行数 int num = statement.executeUpdate(sql ); if (num > 0 ) { System.out.println("插入成功!!" ); } / / 5 关流 statement.close(); conn.close(); } }
小总结:
记住5个步骤的关联和顺序,会读代码
理解url的写法
其中涉及的单词要认识
Driver,Connection,Manager,url,Statement, execute
3 完成增删改 3.1 插入 参考入门案例
3.2 更新 任何的JDBC都是那5个步骤.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.qf.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class Demo2_update { public static void main(String[] args) throws Exception { / / 1 加载驱动 Class.forName("com.mysql.jdbc.Driver"); / / 2 通过驱动管理对象获得连接对象 String url = "jdbc:mysql://localhost:3306/java2217?useSSL=false"; String username = "root"; String password = "123456"; Connection conn = DriverManager.getConnection(url, username, password); / / 3 通过连接对象创建执行语句对象 Statement statement = conn.createStatement( ); / / 4 通过执行语句对象执行sql ,获得结果 String sql = "update tb_user set username = '小孟', phone = '666666' where id = 3"; int num = statement.executeUpdate(sql ); if (num > 0 ) { System.out.println("更新成功!" ); } / / 5 将对象的流关闭 statement.close(); conn.close(); } }
ps: 一定自己主动试错,看报错信息
3.3 删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Demo3_delete { public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); Statement statement = conn.createStatement( ); int num = statement.executeUpdate("delete from tb_user where id = 3"); if (num > 0 ) { System.out.println("删除成功!"); } statement.close(); conn.close(); } }
4 查询结果集ResultSet【重要】
查询返回的是一种虚拟表,Java的JDBC中是使用结果集(ResultSet)来封装这个虚拟表,结果集就是一个集合,内部就存储了列名和每行数据,那么学习查询的重点是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.qf.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class Demo4_select { public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); Statement statement = conn.createStatement( ); String sql = "select id,username,password from tb_user"; / / 执行查询的方法executeQuery,方法返回值是ResultSet ResultSet rs = statement.executeQuery(sql ); while (rs.next()) { / / 通过列下标获得数据 / / int id = rs.getInt(2 ); / / String username = rs.getString(1 ); / / 通过列名获得数据 【推荐】 int id = rs.getInt("id"); String username = rs.getString("username"); System.out.println(id + "-" + username); } statement.close(); conn.close(); } }
每个知识点小问?
1 2 3 4 5 ResultSet是什么? next()有什么特点? if(next())和while(next())有啥区别 ? 如果按下标取值,下标从哪开始,下标顺序是什么顺序? 如果按列名取值,列名根据谁来定?
5 登录案例【重要】
需求:
通过控制台用户输入用户名和密码。
用户输入的用户名和密码作为条件,编写查询 SQL 语句。
select * from user where usename = xxx and password = xxx
如果该用户存在,提示登录成功,反之提示失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package com.qf.jdbc; import com.mysql.jdbc.Driver; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Scanner; public class Demo5_Login { public static void main(String[] args) { / / 1 输入用户名和密码 Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:" ); String username = scanner.nextLine( ); System.out.println("请输入密码:" ); String password = scanner.nextLine( ); / / 2 根据用户名和密码查人 boolean isOk = findUserByLogin(username,password); / / 3 结果 if (isOk) { System.out.println("登录成功!" ); } else { System.out.println("用户名或密码错误!" ); } } / / 使用捕获代码完成 private static boolean findUserByLogin(String username, String password) { Connection conn = null ; Statement statement = null ; boolean isOk = false ; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); statement = conn.createStatement( ); / / 根据用户名和密码查询,注意字符串拼接.特别注意单引号 ResultSet rs = statement.executeQuery("select * from tb_user where username = '"+ username+ "' and password = '"+ password+ "'"); / / 只要有值,就说明数据库有这个信息,登录成功 if (rs.next()) { / / System.out.println("登录成功!" ); int id = rs.getInt("id"); String uname= rs.getString("username"); / / ... System.out.println(id+ "-"+ username); isOk = true ; } else { / / System.out.println("用户名或密码错误!!" ); } }catch (Exception e) { System.out.println("SQL操作出错!" ); e.printStackTrace();/ / 打印异常 } finally { try{ statement.close(); conn.close(); }catch (Exception e) { System.out.println("关流异常" ); e.printStackTrace();/ / 打印异常 } } return isOk; } }
day12 今日内容
0 复习昨日 1 SQL注入问题 2 PreparedStatement 3 完成CRUD练习 4 ORM 5 DBUtil (properties)
6 事务操作
0 复习昨日
已经找人提问…
1 SQL注入 1.1 什么是SQL注入
用户输入的数据中有SQL关键词,导致在执行SQL语句时出现一些不正常的情况.这就是SQL注入!
出现SQL注入是很危险
1.2 避免SQL注入
问题出现在用户输入数据时,里面有关键词,再配合字符串拼接导致出现SQL注入.所以为了避免SQL注入,可以在用户输入数据到SQL之前,先把SQL语句预编译
,预处理后,JDBC就会知道此SQL需要几个参数,后续再将用户输入的数据给参数填充.
这就是PreparedStatement
2 PreparedStatement【重点】
PreparedStatement是Statement的子接口,用来预处理SQL语句
PreparedStatement使用
先写SQL语句,SQL语句中的参数不能直接拼接,而是使用?占位
使用ps预处理SQL语句,处理的?号,ps内部就会知道此SQL语句需要几个参数
再动态给?处填充值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.qf.jdbc; import java.sql.* ; import java.util.Scanner; public class Demo2_LoginPlus { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:" ); String username = scanner.nextLine( ); System.out.println("请输入密码:" ); String password = scanner.nextLine( ); Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); / / 改造SQL ,将拼接变量,变成?占位 String sql = "select * from tb_user where username = ? and password = ?"; System.out.println("处理前: " + sql ); / / 由之前的Statement换成PreparedStatement / / 将改造好的SQL ,传入方法 PreparedStatement ps = conn.prepareStatement(sql ); System.out.println("处理后: " + ps ); / / 给处理好的占位参数赋值 / / ps.setXxx() 给指定Xxx类型赋值 / / 第一个?,下标是1 ps.setString(1 ,username); ps.setString(2 ,password); System.out.println("填充后: " + ps ); / / 【特别注意! ! ! ! 】 此处executeQuery不需要再传入SQL 参数! ! ! ResultSet rs = ps.executeQuery(); if (rs.next()) { System.out.println("登录成功!!" ); } else { System.out.println("用户名或密码错误!" ); } rs.close(); ps.close(); conn.close(); } }
1 2 3 4 5 6 7 8 请输入用户名: 111 请输入密码: 111 ' or ' 1 = 1 处理前: select * from tb_user where username = ? and password = ? 处理后: select * from tb_user where username = * * NOT SPECIFIED * * and password = * * NOT SPECIFIED * * 填充后: select * from tb_user where username = '111' and password = '111\' or \'1=1' 用户名或密码错误!
3 完成CRUD练习 3.1 插入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.qf.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.util.Date; public class Demo3_insert { public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); / / 1 先改造SQL (由拼接变成?) String sql = "insert into tb_user (id,username,password,phone,createTime,money,sex) values (?,?,?,?,?,?,?)"; / / 2 预处理 PreparedStatement ps = conn.prepareStatement(sql ); / / 3 给?处赋值 ps.setInt(1 ,5 ); ps.setString(2 ,"亚鹏"); ps.setString(3 ,"333000"); ps.setString(4 ,"333333"); / / 【特别注意!!!】 setDate的参数,设置是java.sql.Date / / 我们常用的是java.util.Date / / java.sql.Date是java.util.Date的子类 / / java.sql.Date有个构造方法,可以通过毫秒值创建日期 / / 通过常用的java.util.Date获得毫秒值 ps.setDate(5 ,new java.sql.Date(new java.util.Date().getTime())); / / 如果需要设置日期时间 yyyy- MM- dd HH:mm:ss / / 使用ps.setTimestamp(); ps.setDouble(6 ,3000.1 ); ps.setInt(7 ,1 ); / / 【特别注意!!!】 此处不要再填参数 int num = ps.executeUpdate( ); if (num > 0 ) { System.out.println("插入成功" ); } ps.close(); conn.close(); } }
3.2 更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.qf.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.util.Date; public class Demo4_update { public static void main(String[] args) throws Exception{ Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); / / 1 改造sql String sql = "update tb_user set username = ? , createTime = ? , money = ? where id = ?"; / / 2 预处理 PreparedStatement ps = conn.prepareStatement(sql ); / / 3 填充? ps.setString(1 ,"小谢"); Date utilDate = new Date ( ); utilDate.setYear(100 ); utilDate.setMonth(0 ); utilDate.setDate(1 ); long time = utilDate.getTime( ); ps.setDate(2 ,new java.sql.Date(time )); ps.setDouble(3 ,4000.1 ); ps.setInt(4 ,5 ); int num = ps.executeUpdate( ); if (num > 0 ) { System.out.println("更新成功" ); } ps.close(); conn.close(); } }
3.3 删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void main(String[] args) throws Exception{ Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false", "root", "123456"); / / 1 改造sql String sql = "delete from tb_user where id = ?"; / / 2 预处理 PreparedStatement ps = conn.prepareStatement(sql ); / / 3 填充? ps.setInt(1 ,5 ); int num = ps.executeUpdate( ); if (num > 0 ) { System.out.println("删除成功" ); } ps.close(); conn.close(); }
4 事务处理
事务是逻辑一组操作,要么全部成功,要么全部失败!
使用mysql客户端操作事务
因为mysql支持事务,且每句话都在事务内,且自动提交
所以关闭自动提交事务,手动开启事务 start transaction
正常写sql/执行sql
一切正常,提交事务 commit
如果不正常,要回滚 rollback
JDBC也可以完成事务操作
conn.setAutoCommit(false) 关闭自动提交,就相当于是开启手动管理
正常的处理sql
一切正常,提交事务 conn.commit()
如果不正常,回滚 conn.rollback()
演示: 以转账案例演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package com.qf.tx;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;public class Demo6_TX { public static void main (String[] args) { Connection conn = null ; PreparedStatement ps1 = null ; PreparedStatement ps2 = null ; try { Class.forName("com.mysql.jdbc.Driver" ); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false" , "root" , "123456" ); conn.setAutoCommit(false ); String sql1 = "update account set money = money - 100 where id = 1" ; ps1 = conn.prepareStatement(sql1); int num = ps1.executeUpdate( ); if (num > 0 ) { System.out.println("张三转账(-100)完成!" ); } System.out.println(1 /0 ); String sql2 = "update account set money = money + 100 where id = 2" ; ps2 = conn.prepareStatement(sql2); int num2 = ps2.executeUpdate( ); if (num2 > 0 ) { System.out.println("李四转账(+100)完成!" ); } conn.commit(); } catch (Exception e) { try { conn.rollback(); }catch (Exception e2) { System.out.println("回滚事务异常!!" ); e2.printStackTrace(); } System.out.println("SQL异常!!!" ); e.printStackTrace( ); } finally { try { ps1.close( ); ps2.close( ); conn.close( ); } catch (Exception e) { System.out.println("关流时有异常!!" ); e.printStackTrace( ); } } } }
另外发现: 建立与Mysql连接后,关流之前,可以执行很多次SQL语句
5 ORM【重点】 5.1 什么是ORM
目前使用JDBC完成了CRUD,但是现在是进行CRUD,增删改方法要设计很多参数,查询的方法需要设计集合才能返回.
在实际开发中,我们需要将零散的数据封装到对象处理.
ORM (Object Relational Mapping) 对象关系映射
是指数据库表
与Java的实体类
有关系,可以进行映射
数据库表 –> Java的类
字段 –> 类的属性
id int –> private int id;
username varchar –> private String username;
…
一行数据 –> 类的对象
5.2 实体类
实体类: 数据表中零散数据的载体,用来封装数据.
表名 设计 类名
将列名设计成属性名
id –> id
create_time –> createTime (下划线转驼峰)
将列的数据类型设计成属性的数据类型
给类提供对应set get
一般项目中一个表就会对应一个实体类,所有的实体类都会放在model/entity /pojo/javabeen包结构中
将来写项目,数据库设计完,搭建完项目,第一件事件就是根据表结构,创建实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.qf.model; public class User { private int id; private String username; private String password; private String phone; private Date createTime; private double money; private int sex; }
5.3 使用ORM完成CRUD 5.3.1 查询使用ORM封装数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package com.qf.orm;import com.qf.model.User;import java.sql.*;import java.util.Scanner;public class Demo7_Login_ORM { public static void main (String[] args) throws Exception{ Scanner scanner = new Scanner (System.in); System.out.println("请输入用户名:" ); String username = scanner.nextLine( ); System.out.println("请输入密码:" ); String password = scanner.nextLine( ); Class.forName("com.mysql.jdbc.Driver" ); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false" , "root" , "123456" ); String sql = "select * from tb_user where username = ? and password = ?" ; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1 ,username); ps.setString(2 ,password); ResultSet rs = ps.executeQuery( ); User user = null ; if (rs.next()) { int id = rs.getInt("id" ); String uname = rs.getString("username" ); String pwd = rs.getString("password" ); String phone = rs.getString("phone" ); Date createTime = rs.getDate("createTime" ); double money = rs.getDouble("money" ); int sex = rs.getInt("sex" ); user = new User (); user.setId(id); user.setUsername(uname); user.setPassword(pwd); user.setPhone(phone); user.setCreateTime(createTime); user.setMoney(money); user.setSex(sex); } if (user != null ) { System.out.println("登录成功!" ); System.out.println("个人信息为:" + user ); } else { System.out.println("登录失败!" ); } } }
5.3.2 插入使用ORM封装数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.qf.orm;import com.qf.model.User;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.util.Date;public class Demo8_Insert_ORM { public static void main (String[] args) { User user = new User ( ); user.setId(6 ); user.setUsername("李冰" ); user.setPassword("123456" ); user.setCreateTime(new Date ()); user.setMoney(4000.1 ); user.setSex(2 ); user.setPhone("222333" ); insert(user); } public static void insert (User user) { Connection conn = null ; PreparedStatement ps = null ; try { Class.forName("com.mysql.jdbc.Driver" ); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2217?useSSL=false" , "root" , "123456" ); String sql = "insert into tb_user (id,username,password,phone,createTime,money,sex) values (?,?,?,?,?,?,?)" ; ps = conn.prepareStatement(sql); ps.setInt(1 ,user.getId()); ps.setString(2 ,user.getUsername()); ps.setString(3 ,user.getPassword()); ps.setString(4 ,user.getPhone()); ps.setDate(5 ,new java .sql.Date(user.getCreateTime().getTime())); ps.setDouble(6 ,user.getMoney()); ps.setInt(7 ,user.getSex()); int num = ps.executeUpdate( ); if (num > 0 ) { System.out.println("注册成功!" ); } } catch (Exception e) { System.out.println("SQL异常!!!" ); e.printStackTrace(); } finally { try { ps.close(); conn.close(); }catch (Exception e) { System.out.println("关流异常!!" ); e.printStackTrace(); } } } }
6 DBUtil【理解,会用】
DBUtil操作数据库的工具类,因为发现每次操作数据库,JDBC的步骤第1,2,5步完全重复的,即加载驱动,获得连接对象,已经最后的关流是每次都要写但每次都是一样的!!!!
现在设计工具类,简化第1,2,5步
设计个方法,调用直接获得连接对象
设计个方法,调用直接关闭全部的流对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 package com.qf.util; import java.io.InputStream; import java.sql.* ; import java.util.Properties; public class DBUtil { / / 创建Properties类对象,专用于操作properties文件 private static final Properties properties = new Properties(); static { / / 通过反射的技术获得字节码文件 / / 再通过字节码文件将配置文件读取成输入流 InputStream inputStream = DBUtil.class.getResourceAsStream("/jdbc.properties"); try { / / 再通过流获得其中数据 properties.load(inputStream); / / 从properties对象取值 Class.forName(properties.getProperty("driverClass")); } catch (Exception e) { System.out.println("加载驱动异常!!" ); e.printStackTrace( ); } } public static Connection getConnection() { Connection conn = null ; try{ conn = DriverManager.getConnection(properties.getProperty("url"),properties.getProperty("username") ,properties.getProperty("password") ); } catch (Exception e) { System.out.println("获得连接出异常!!!" ); e.printStackTrace(); } return conn; } public static void closeAll(Connection conn, Statement s) { if (conn != null ) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace( ); } } if (s != null ) { try { s.close(); } catch (SQLException throwables) { throwables.printStackTrace( ); } } } public static void closeAll(Connection conn, Statement s, ResultSet rs){ if (conn != null ) { try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace( ); } } if (s != null ) { try { s.close(); } catch (SQLException throwables) { throwables.printStackTrace( ); } } if (rs != null ) { try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace( ); } } } }
在src下创建jdbc.properties文件
1 2 3 4 driverClass =com.mysql.jdbc.Driver url =jdbc:mysql://localhost:3306/java2217?useSSL=false username =root password =123456
day13 今日内容
0 复习昨日 1 讲作业 2 数据库连接池(druid) 3 反射 4 改造DBUtil 5 完成CRUD练习
0 复习昨日
1 sql注入
2 预处理语句
3 事务操作
4 DBUtil
1 作业【重要】
利用ORM完成,以下的几个方法非常重要,将来写项目就是这些操作
写项目步骤
搭建环境
开发数据库层的代码
数据库层代码,一般称为dao (data access object)
操作的是User表,所以类UserDao
1 2 3 4 5 6 7 8 9 10 CREATE TABLE `tb_user` ( `id` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '用户编号' , `username` varchar (10 ) DEFAULT NULL COMMENT '用户名' , `password` varchar (10 ) DEFAULT NULL COMMENT '密码' , `phone` varchar (11 ) DEFAULT NULL COMMENT '手机号' , `createTime` date DEFAULT NULL COMMENT '注册时间' , `money` double (10 ,2 ) DEFAULT NULL COMMENT '账户余额' , `sex` int (1 ) DEFAULT NULL COMMENT '性别 1男2女' , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 8 DEFAULT CHARSET= utf8;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 根据id从数据库查询出用户信息 public User findUserById(int id) 2 向数据库插入一个用户,返回是否插入成功 public boolean addUser(User user) 3 通过id删除用户数据,返回boolean public boolean deleteById(int id) 4 设计方法,查询全部数据,返回值是List集合,集合中是全部用户数据 publicList<User> findAll() 5 通过id更改用户数据 public boolean updateById(User user) 注意:根据id更新,即参数User对象中一定有id属性值 更新user表的数据,根据User对象的属性更新,如果属性值是null则不跟更新,不为null就更新对应的字段 返回值是受影响的行数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 package com.qf.dao;import com.qf.model.User;import com.qf.util.DBUtil;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Statement;import java.util.ArrayList;import java.util.List;public class UserDao { public User findUserById (int id) { Connection conn = DBUtil.getConnection( ); PreparedStatement ps = null ; ResultSet rs = null ; User user = null ; try { ps = conn.prepareStatement("select * from tb_user where id = ?" ); ps.setInt(1 , id); rs = ps.executeQuery( ); if (rs.next( )) { user = new User ( ); user.setId(rs.getInt("id" )); user.setPhone(rs.getString("phone" )); user.setUsername(rs.getString("username" )); user.setSex(rs.getInt("sex" )); user.setMoney(rs.getDouble("money" )); user.setPassword(rs.getString("password" )); user.setCreateTime(rs.getDate("createTime" )); } } catch (Exception e) { e.printStackTrace( ); } finally { DBUtil.closeAll(conn, ps, rs); } return user; } public List<User> findAll () { Connection conn = DBUtil.getConnection( ); PreparedStatement ps = null ; ResultSet rs = null ; User user = null ; ArrayList<User> list = new ArrayList <>( ); try { ps = conn.prepareStatement("select * from tb_user" ); rs = ps.executeQuery( ); while (rs.next( )) { user = new User ( ); user.setId(rs.getInt("id" )); user.setPhone(rs.getString("phone" )); user.setUsername(rs.getString("username" )); user.setSex(rs.getInt("sex" )); user.setMoney(rs.getDouble("money" )); user.setPassword(rs.getString("password" )); user.setCreateTime(rs.getDate("createTime" )); list.add(user); } } catch (Exception e) { e.printStackTrace( ); } finally { DBUtil.closeAll(conn, ps, rs); } return list; } public boolean addUser (User user) { Connection conn = DBUtil.getConnection( ); PreparedStatement ps = null ; int num = 0 ; try { String sql = "insert into tb_user (id,username,password,phone,createTime,money,sex) values (?,?,?,?,?,?,?)" ; ps = conn.prepareStatement(sql); ps.setInt(1 , user.getId( )); ps.setString(2 , user.getUsername( )); ps.setString(3 , user.getPassword( )); ps.setString(4 , user.getPhone( )); ps.setDate(5 , new java .sql.Date(user.getCreateTime( ).getTime( ))); ps.setDouble(6 , user.getMoney( )); ps.setInt(7 , user.getSex( )); num = ps.executeUpdate( ); } catch (Exception e) { e.printStackTrace( ); } finally { DBUtil.closeAll(conn, ps); } return num > 0 ? true : false ; } public boolean deleteById (int id) { Connection conn = DBUtil.getConnection( ); PreparedStatement ps = null ; int num = 0 ; try { String sql = "delete from tb_user where id = ?" ; ps = conn.prepareStatement(sql); ps.setInt(1 , id); num = ps.executeUpdate( ); } catch (Exception e) { e.printStackTrace( ); } finally { DBUtil.closeAll(conn, ps); } return num > 0 ? true : false ; } public boolean updateById1 (User user) { Connection conn = DBUtil.getConnection( ); PreparedStatement ps = null ; int num = 0 ; try { String sql = "update tb_user set username = ?,password = ?," + "phone = ?,createTime = ?,money = ? ," + "sex = ? where id = ?" ; ps = conn.prepareStatement(sql); ps.setString(1 , user.getUsername( )); ps.setString(2 , user.getPassword( )); ps.setString(3 , user.getPhone( )); ps.setDate(4 , new java .sql.Date(user.getCreateTime( ).getTime( ))); ps.setDouble(5 , user.getMoney( )); ps.setInt(6 , user.getSex( )); ps.setInt(7 , user.getId( )); num = ps.executeUpdate( ); } catch (Exception e) { e.printStackTrace( ); } finally { DBUtil.closeAll(conn, ps); } return num > 0 ? true : false ; } public boolean updateById2 (User user) { Connection conn = DBUtil.getConnection( ); Statement statement = null ; int num = 0 ; try { String sql = "update tb_user set " ; if (user.getUsername( ) != null ) { sql += "username = '" + user.getUsername( ) + "'," ; } if (user.getPassword( ) != null ) { sql += "password = '" + user.getPassword( ) + "' ," ; } if (user.getPhone( ) != null ) { sql += "phone = '" + user.getPhone( ) + "' ," ; } System.out.println("字段拼接完的sql " + sql); int index = sql.lastIndexOf("," ); String newSql = sql.substring(0 , index); System.out.println("截取,后的sql " + newSql); newSql += " where id = " + user.getId( ); System.out.println("最终的sql " + newSql); statement = conn.createStatement( ); num = statement.executeUpdate(newSql); } catch (Exception e) { e.printStackTrace( ); } finally { DBUtil.closeAll(conn,statement); } return num > 0 ? true : false ; } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.qf.test;import com.qf.dao.UserDao;import com.qf.model.User;import java.util.Date;import java.util.List;public class TestUserDao { public static void main (String[] args) { UserDao userDao = new UserDao ( ); User user = new User ( ); user.setId(2 ); user.setUsername("ROOT" ); user.setPassword("654321" ); boolean isOk = userDao.updateById2(user); System.out.println(isOk ); } }
小结
1 2 3 4 5 6 1 DAO是什么 2 关于数据库操作的类,应该放在什么包,什么类中 3 要将数据封装成对象(ORM) 4 学会在编码时通过输出语句确定结果是否符合预期 5 学会DEBUG 6 记住以上5个CRUD思路
2 数据库连接池
目前数据库连接是使用是建立连接,用完直接关闭连接.即需要不断创建和销毁连接.就会消耗系统资源.借鉴线程池的思想,设计出数据库连接池
.
在程序初始时,预先创建好指定数量的数据库连接对象,存储连接池。需要用时就去取,用完就放回去即可。就会不会频繁创建和销毁,从而节省系统资源。
使用上的线程池有很多
2.1 Druid数据库连接池
Druid是阿里开源技术,性能很好
使用步骤
导入依赖druid.jar包
创建一个jdbc.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 driverClass =com.mysql.jdbc.Driver url =jdbc:mysql://localhost:3306/java2217?useSSL=false username =root password =123456 initialSize =10 maxActive =50 minIdle =5 maxWait =5000
修改之前的DBUtil
1 2 3 4 5 6 7 public class DBUtil { private static final Properties properties = new Properties (); private static DruidDataSource dataSource;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static { try { InputStream inputStream = DBUtil.class.getResourceAsStream("/jdbc.properties" ); properties.load(inputStream); dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { System.out.println("加载驱动异常!!" ); e.printStackTrace( ); } } public static Connection getConnection () { Connection conn = null ; try { conn = dataSource.getConnection(); } catch (Exception e) { System.out.println("获得连接出异常!!!" ); e.printStackTrace(); } return conn; }
}
3 反射(reflect)
JAVA反射机制是在运行状态
中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对 象,都能够调用它的任意一个方法和属性
这种动态获取的信息
以及动态调用对象的方法
的功能称为java 语言的反射机制。
反射是在程序运行过程中拿到类的字节码文件,进而获得类中的属性,方法等.
3.1 获得类的字节码文件
Object类的方法 getClass()
类的静态属性 Xxx.class
Class类的静态方法Class.forName(“xxx”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void getClassFile () throws ClassNotFoundException { Class<?> clazz = Class.forName("com.qf.model.User" ); Class<User> clazz2 = User.class; User user = new User ( ); Class<? extends User > clazz3 = user.getClass( ); if (clazz.equals(clazz2) && clazz2.equals(clazz3)) { System.out.println("是同一个字节码文件" ); } else { System.out.println("不是" ); } }
3.2 获得并设置属性(Field)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public static void getAndSetFields () throws Exception { Class<User> clazz = User.class; Field id1 = clazz.getDeclaredField("id" ); System.out.println(id1 ); Field[] fields = clazz.getFields( ); System.out.println(fields.length ); Field[] declaredFields = clazz.getDeclaredFields( ); System.out.println(declaredFields.length ); String name = id1.getName( ); System.out.println("name = " + name ); int modifiers = id1.getModifiers( ); System.out.println("访问修饰符: " + modifiers ); id1.setAccessible(true ); User user = new User ( ); int value = id1.getInt(user); System.out.println("id = " + value ); id1.setInt( user,11 ); System.out.println(user ); }
3.3 获得并设置方法(Method)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void getAndSeMethod () throws Exception { Class<User> clazz = User.class; Method m1 = clazz.getMethod("m1" ); Method m1_ = clazz.getMethod("m1" ,int .class); int count = m1.getParameterCount( ); int count_ = m1_.getParameterCount( ); Object ret = m1.invoke(new User ( )); System.out.println("m1()执行后的返回值:" + ret ); m1_.invoke(new User (),222 ); }
3.4 获得并设置构造方法(Constructor)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void getAndSeConstructor () throws Exception { Class<User> clazz = User.class; Constructor<User> constructor = clazz.getConstructor( ); User user = constructor.newInstance( ); System.out.println(user ); User user1 = clazz.newInstance( ); }
4 使用反射封装DBUtil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 package com.qf.util;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.pool.DruidDataSourceFactory;import java.io.File;import java.io.InputStream;import java.lang.reflect.Field;import java.sql.*;import java.util.ArrayList;import java.util.List;import java.util.Properties;public class DBUtil { private static final Properties properties = new Properties ( ); private static DruidDataSource dataSource; static { try { InputStream inputStream = DBUtil.class.getResourceAsStream("/jdbc.properties" ); properties.load(inputStream); dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { System.out.println("加载驱动异常!!" ); e.printStackTrace( ); } } public static Connection getConnection () { Connection conn = null ; try { conn = dataSource.getConnection( ); } catch (Exception e) { System.out.println("获得连接出异常!!!" ); e.printStackTrace( ); } return conn; } public static void closeAll (Connection conn, Statement s) { if (conn != null ) { try { conn.close( ); } catch (SQLException throwables) { throwables.printStackTrace( ); } } if (s != null ) { try { s.close( ); } catch (SQLException throwables) { throwables.printStackTrace( ); } } } public static void closeAll (Connection conn, Statement s, ResultSet rs) { if (conn != null ) { try { conn.close( ); } catch (SQLException throwables) { throwables.printStackTrace( ); } } if (s != null ) { try { s.close( ); } catch (SQLException throwables) { throwables.printStackTrace( ); } } if (rs != null ) { try { rs.close( ); } catch (SQLException throwables) { throwables.printStackTrace( ); } } } public static <T> T selectOne (String sql, Class<T> t, Object... args) { Connection conn = getConnection( ); PreparedStatement ps = null ; ResultSet rs = null ; T target = null ; try { ps = conn.prepareStatement(sql); for (int i = 0 ; args != null && i < args.length; i++) { ps.setObject(i + 1 , args[i]); } rs = ps.executeQuery( ); while (rs.next( )) { target = t.newInstance( ); Field[] declaredFields = t.getDeclaredFields( ); for (int i = 0 ; i < declaredFields.length; i++) { Field field = declaredFields[i]; Object value = rs.getObject(field.getName( )); field.setAccessible(true ); field.set(target, value); } } } catch (Exception e) { e.printStackTrace( ); } finally { closeAll(conn, ps, rs); } return target; } public static <T> List<T> selectAll (String sql, Class<T> t, Object... args) { Connection conn = getConnection( ); PreparedStatement ps = null ; ResultSet rs = null ; T target = null ; ArrayList<T> list = new ArrayList <>( ); try { ps = conn.prepareStatement(sql); for (int i = 0 ; args != null && i < args.length; i++) { ps.setObject(i + 1 , args[i]); } rs = ps.executeQuery( ); while (rs.next( )) { target = t.newInstance( ); Field[] declaredFields = t.getDeclaredFields( ); for (int i = 0 ; i < declaredFields.length; i++) { Field field = declaredFields[i]; Object value = rs.getObject(field.getName( )); field.setAccessible(true ); field.set(target, value); } list.add(target); } } catch (Exception e) { e.printStackTrace( ); } finally { closeAll(conn, ps, rs); } return list; } public static boolean update (String sql, Object... args) { Connection conn = getConnection( ); PreparedStatement ps = null ; int num = 0 ; try { ps = conn.prepareStatement(sql); for (int i = 0 ; args != null && i < args.length; i++) { ps.setObject(i + 1 , args[i]); } num = ps.executeUpdate( ); } catch (Exception e) { e.printStackTrace( ); } finally { closeAll(conn, ps); } return num > 0 ? true : false ; } }
day14 今日内容
0 复习昨日
1 maven
2 tomcat
3 创建项目
0 复习昨日
1 单词写5遍 argument 参数 parameter 参数 access 访问 field 字段 invoke 调用 illegal 非法
invalid 无效 column 列 property 属性 DataSource 数据源
2 数据库连接池有啥好处
3 获得字节码文件的方式 Class.forName(“”) Xxx.class 对象.getClass()
4 封装 隐藏实现的细节,对外提供访问的方法 方法的封装 类的封装 工具类的封装
继承
多态 方法的参数列表是父类 抽象 接口
1 Maven 1.0 引言
之前写项目时,会有不同的问题
jar包 管理(项目中有很多jar包)
需要自己找jar包,下载
手动导入项目中
jar包版本更新….
占磁盘空间
重复量大
项目结构不规范
….
1.1 介绍
项目管理工具,统一项目结构,配置文件,依赖,部署,测试等等
Maven这个单词来自于意第绪语(犹太语),意为知识的积累,最初在Jakata Turbine项目中用来简化构建过程。当时有一些项目(有各自Ant build文件),仅有细微的差别,而JAR文件都由CVS 来维护。于是希望有一种标准化的方式构建项目,一个清晰的方式定义项目的组成,一个容易的方式发布项目的信息,以及一种简单的方式在多个项目中共享JARs。
1.2 下载
网址 Maven – Download Apache Maven
下载地址 Index of /dist/maven/maven-3 (apache.org)
1.3 安装 1.3.1 解压
特别注意: 尽量不要有中文路径
1 2 3 4 5 解压后有几个文件夹 - bin 运行maven命令的脚本 - boot 运行是需要类库 - conf 配置,有关于maven的配置文件 - lib 运行是需要的jar包
1.3.2 配置环境变量
系统变量创建: MAVEN_HOME 值是maven安装路径
系统变量path添加 %MAVEN_HOME%\bin
1.3.3 测试 打开cmd,输入mvn -v
1.4 仓库
maven项目管理工具,管理依赖(jar包),实现依赖的复用.
maven有仓库,将依赖放入仓库,每个项目都去复用
本地仓库
自己电脑上
需要依赖的时候,会先从本地仓库中,如果找不到就会去中央仓库找,下载到本地仓库
中央仓库
位于国外服务器,包含绝大部分依赖
可能有时候访问比较慢
公共仓库
1.5 Maven配置 1.5.1 修改仓库位置
maven安装好后,默认本地仓库在c盘,要修改为其他地方
修改maven的配置文件(conf\settings.xml)
1 2 <localRepository > D:\repository</localRepository >
ps: 记得保存,ctrl+s
1.5.2 设置镜像
依赖会先从本地仓库找,本地没有会从中央仓库下载到本地仓库,中央仓库访问很慢,所以需要设置国内镜像,访问就很快!
1 2 3 4 5 6 7 8 9 10 11 <mirror > <id > aliyun</id > <mirrorOf > central</mirrorOf > <name > Nexus aliyun</name > <url > http://maven.aliyun.com/nexus/content/groups/public</url > </mirror >
2 IDEA - MAVEN 2.1 idea关联maven
找到build
2.2 创建java项目
找到maven选项
设置信息
2.3 java项目结构
1 2 3 4 5 6 7 8 |-项目名 |---src |------main |---------java |---------resources |------test |----------java |---pom.xml
2.4 pom
pom 项目对象模型,这是一个xml文件(ps: xml文件一种文件格式,类似HTML是标签形式的)
pom文件内定义
项目信息
项目名
组织名
版本
打包方式
默认是jar , 普通java项目
可以指定为war, 即web项目
项目依赖
构建工具
1 2 3 4 5 6 <groupId > com.qf</groupId > <artifactId > day45_java</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > jar</packaging >
1 2 3 4 5 6 7 8 9 <dependencies > </dependencies >
2.5 导入依赖 2.5.1 查找依赖
官方提供一中央仓库网站,网站中有所有jar包的依赖信息,就可以搜索依赖
Maven Repository: Search/Browse/Explore (mvnrepository.com)
2.5.2 使用依赖
将复制的依赖坐标,粘贴到pom文件
1 2 3 4 5 6 7 8 <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > </dependencies >
还可以进入仓库中去查看,是否下载成功
2.6 测试 使用之前jdbc测试
使用maven创建java项目
导入依赖
编码
java文件夹写java代码
resources 文件夹写配置文件
测试
3 JavaWeb
JavaWeb开发就是
前端页面发请求
后台服务器接收请求,将请求中数据发送到数据库
数据库处理CRUD
数据库处理完再响应给服务器
服务器根据结果再响应数据到浏览器
项目开发的架构
C/S (Client / Server)
必须要开发客户端软件(QQ,微信,钉钉,LOL…)
优点: 性能好(画质,交互,流程度),安全度高
一般用于游戏/音视频软件
缺点: 软件更新维护升级很麻烦
B/S (Browser / Server)
只需要一个浏览器
一般用于功能不复杂,比如微博/淘宝/京东
优点: 更新维护只需重启服务器
缺点: 图形显示,流畅度,安全性相对比较低
目前我们学习Java是为了开发B/S架构的项目
4 服务器
服务器也称为web服务器,是运行及发布web应用
的容器.
只有将开发的项目放到服务器中,才可以通过http请求访问到数据.
常见的web服务器
Tomcat 主流,免费,并发量500左右
Jetty 效率会比Tomcat高,淘宝用的就这个
Resin 新浪在用
WebLogic
Apache
…
5 Tomcat
Tomcat 服务器是一个免费的开放源代码
的Web 应用服务器,属于轻量级
应用服务器 ,在中小型系统
和并发访问用户不是很多
的场合下被普遍使用,是开发和调试JSP 程序的首选。
5.1 下载
官网 Apache Tomcat® - Welcome!
5.2 安装
解压即可使用,特别注意,解压路径中不要有中文路径!
1 2 3 4 5 6 7 bin 放tomcat运行命令 conf 配置文件 lib 运行所需库,jar包等 logs 运行日志 temp 临时文件 webapps 【重要】存放web项目的路径 work 运行时产生文件此处
5.3 启动服务器 进入安装路径的bin目录下,执行(双击)startup.bat命令
5.4 访问服务器
服务器是在本地,所以访问ip是localhost,tomcat端口默认是8080,即完整服务器路径
http://localhost:8080/index.jsp
5.5 关闭服务器
只需要关闭服务器黑窗口
5.6 特别说明
现在进行的操作,只是证明tomcat装好可以使用,
等后续需要idea配置tomcat进行启动,访问,停止
6 IDEA - Tomcat
IDEA关联Tomcat是要为每个web项目
关联服务器
6.1 maven创建javaweb项目 6.1.1 使用模板创建【推荐】
模板创建并不完整
手动补全目录 补上src/test目录
补上src/main/java目录
补上src/main/resources目录
图略
完整结构如下
6.1.2 不使用模板创建javaweb项目 像创建java项目一样,创建javaweb项目
这样创建出的是java项目,改造成javaweb项目
但是这样创建的web文件夹位置跟maven规范的不一致
这样不推荐
6.1.3 不使用模板,也不使用框架支持 这种方式是纯手动改造java项目为javaweb项目
手动创建webapp目录,并在在webapp目录下创建WEB-INF目录,并在其下创建web.xml文件,内容如下
1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version ="3.1" ></web-app >
最后,在webapp目录下,创建一个index.jsp文件(jsp文件,理解为html)
在项目结构(project structure)中配置该webapp路径
6.2 web项目设置Tomcat(部署项目)
day15 今日内容
0 复习昨日 1 Servlet基础 1.1 Servlet介绍 1.2 第一个Servlet 1.3 流程分析 1.4 使用细节 1.5 映射细节 1.6 生命周期 2 HttpServlet 2.1 HTTP请求、响应、状态码 2.2 GET和POST的区别 2.3 HttpServlet
0 复习昨日
1 maven创建-java项目结构 2 maven创建-javaweb项目结构 3 tomcat的端口 8080 ip: 地址,通过地址找到这台电脑/服务器 端口: 应用程序的编号,通过端口找到app
4 tomcat服务器部署项目启动后,访问路径
是? 协议://ip:端口/项目名/页面http://localhost:8080/day45_web/index.html http://localhost:8080/index.html
5 addres localhost:8080 is already in use 什么意思 端口占用,解决方案: 1) 端口查杀
netstat -ano | findstr 8080 taskkill /f /pid 进程号 2) 改端口号
(我们自己设计端口,最好是8000以上 3) artifact 工件/项目 dependencies 依赖,复数 dependency 依赖,单数
1 Servlet 1.1 介绍
javaweb开发,就是需要服务器接收前端发送的请求
,以及请求中的数据,经过处理(jdbc操作),然后向浏览器做出响应
.
我们要想在服务器中写java代码来接收请求,做出响应,我们的java代码就得遵循tomcat开发规范
因此Tomcat提供了开发的规范,就是servlet.
Servlet就是运行在服务器上的程序,可交互式的接收服务器的请求,并可以做出响应
总结Servlet的作用:
运行在服务器,是一个服务器端的程序
接收客户端请求,向客户端做出响应
动态网页(jsp)
1.2 第一个Servlet程序 1.2.1 创建web项目
补全目录结构
1.2.2 pom依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.qf</groupId > <artifactId > day46_servlet</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > war</packaging > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.8</maven.compiler.source > <maven.compiler.target > 1.8</maven.compiler.target > </properties > <dependencies > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 3.1.0</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > javax.servlet.jsp-api</artifactId > <version > 2.3.1</version > </dependency > </dependencies > </project >
1.2.3 编写Servlet
实现javax.servlet.Servlet接口
重写方法
在核心方法service()里面完成接收请求,做出响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.qf.servlet;import javax.servlet.*;import java.io.IOException;public class MyServlet1 implements Servlet { @Override public void init (ServletConfig config) throws ServletException {} @Override public ServletConfig getServletConfig () { return null ; } @Override public void service (ServletRequest req, ServletResponse res) throws ServletException, IOException { String ip = req.getRemoteAddr( ); System.out.println("ip = " +ip ); res.getWriter().write("i'm Response,Hello" ); } @Override public String getServletInfo () { return null ; } @Override public void destroy () {} }
1.2.4 配置Servlet
因为服务器中会有很多servlet,浏览器发请求如何确定访问哪一个servlet?
此时就需要做一个映射: 请求路径和servlet类的映射,即发出的请求由哪个servlet类来处理
配置需要写在webapp/WEB-INF/web.xml中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version ="3.1" > <servlet > <servlet-name > servlet1</servlet-name > <servlet-class > com.qf.servlet.MyServlet1</servlet-class > </servlet > <servlet-mapping > <servlet-name > servlet1</servlet-name > <url-pattern > /s1</url-pattern > </servlet-mapping > </web-app >
浏览器发出请求,经过web.xml中配置的信息,
匹配url-pattern>/s1</url-pattern,有该路径则正常访问,无该路径报404
通过servlet-name找到servlet类
再通过servlet-class,找到servlet类路径
该servlet就可以执行service()
1.2.5 部署项目
web项目已经开发完毕,将项目部署到服务器Tomcat
配置Tomcat
部署项目
启动
发出请求
http://localhost:8080/day46/s1
day16 今日内容
0 复习昨日
1 接收请求
2 处理响应
0 复习昨日
HTTP请求中
HTTP响应中
1 接收请求
浏览器发出请求,经过web.xml映射匹配,找到Servlet对应的方法(doGet/doPost),接收请求数据,可以接收请求中的请求行,请求头,请求正文
浏览器发出请求
经过web.xml映射匹配
doGet/doPost
前端是get请求,就重写doGet
前端是post请求,就重写doPost
如何接收数据
需求: html页面中写一个表单,发送请求,后台服务器接收所有请求数据
1.1 编写页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="/day47/req" method ="get" > 用户名 <input type ="text" name ="username" > <br > 密码 <input type ="password" name ="password" > <br > 性别 <input type ="radio" name ="sex" value ="1" > 男 <input type ="radio" name ="sex" value ="2" > 女<br > 技能<input type ="checkbox" name ="skill" value ="Java" > Java <input type ="checkbox" name ="skill" value ="JavaScript" > JavaScript <input type ="checkbox" name ="skill" value ="SSM" > SSM<br > 学历<select name ="xueli" > <option value ="gaozhong" > 高中</option > <option value ="dazhuan" > 大专</option > <option value ="benke" > 本科</option > </select > <input type ="submit" value ="提交" > </form > </body > </html >
1.2 编写Servlet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package com.qf.servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Enumeration;public class MyServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod( ); System.out.println("method = " +method); String requestURI = req.getRequestURI( ); StringBuffer requestURL = req.getRequestURL( ); System.out.println("requestURI = " + requestURI); System.out.println("requestURL = " + requestURL); System.out.println("--------------------------------------" ); String host = req.getHeader("Host" ); System.out.println("host = " + host); Enumeration<String> keys = req.getHeaderNames( ); while (keys.hasMoreElements()) { String key = keys.nextElement( ); String value = req.getHeader(key); System.out.println(key + " : " + value); } System.out.println("--------------------------------------" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); String sex = req.getParameter("sex" ); String xueli = req.getParameter("xueli" ); System.out.println("username = " + username); System.out.println("password = " + password); System.out.println("sex = " + sex); System.out.println("xueli = " + xueli); String[] skills = req.getParameterValues("skill" ); for (int i = 0 ; skills != null && i < skills.length; i++) { System.out.println("skill[" +(i+1 )+"] = " + skills[i]); } } }
1.3 配置web.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version ="3.1" > <welcome-file-list > <welcome-file > index.html</welcome-file > </welcome-file-list > <servlet > <servlet-name > servlet</servlet-name > <servlet-class > com.qf.servlet.MyServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > servlet</servlet-name > <url-pattern > /req</url-pattern > </servlet-mapping > </web-app >
1.4 部署项目 1.5 启动测试 2 做出响应
做出响应是通过HttpServletResponse对象
响应行
响应头
响应信息,其中有一个cookie后续会用到,以及编码格式
响应正文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 resp.setContentType("text/html;charset=utf-8" ); PrintWriter out = resp.getWriter( );out.write("<html>" ); out.write(" <head>" ); out.write(" <title>这是响应</title>" ); out.write(" </head>" ); out.write(" <body>" ); out.write(" <div style='background-color:red;width:500px;height:500px;font-size:50px'>" ); out.write(" 这是响应,欢迎" +username); out.write(" </div>" ); out.write(" </body>" ); out.write("</html>" );
3 乱码解决
请求乱码
1 req.setCharacterEncoding("utf-8" );
响应乱码
1 resp.setContentType("text/html;charset=utf-8" );
day17 今日内容
周一 0 复习上周 1 本周计划 2 MVC和三层架构 3 Login案例 4 请求转发 5 重定向
0 复习昨日
1 jdbc五大步骤 1) 注册驱动(反射) 2) 获得连接 3) 获得执行sql对象 4) 执行SQL 5) 关流 2 什么是SQL注入 通过SQL关键词,在执行SQL时出现不正常的情况 3 PreparedStatement怎么使用,有什么特点 怎么使用? 之前拼接sql参数的地方,现在使用?占位,经过预处理后,再给?处赋值 有什么特点? 1) 执行时不需要再给executeQuery()传参数 2) 可以避免SQL注入的问题 3) 向?赋值的时候,自动给字符串拼接单引号 mybatis # PreparedStatement ,字符串’’ $ Statement 4 什么是servlet servlet是运行在服务上的程序 servlet主要功能是: 接收请求,做出响应
5 Http请求方式有哪些 get post 6 Http请求报文都有哪些内容 请求头,请求行,请求正文(数据)
针对不同的请求方式,后台有哪些请求方法? doGet() doPost() 7 后台接收请求内容的方法有哪些 req.getMethod() req.getRequestURL() req.getRequestURI() req.getParameter(name属性的值);//获得请求数据 req.getParameterValues();
8 前端如何发送数据 form表单,标签得设置name属性
1 2 3 4 5 <form action ="/day48/login" method ="get" > <input type ="text" name ="username" /> <input type ="text" name ="password" /> <input type ="submit" value ="提交" /> </form >
ajax a标签
1 <a href ="/day48/login?username=root&pwd=555" > 登录</a >
9 前端后后台之间如何映射? 通过web.xml配置8行代码
1 2 3 4 5 6 7 8 <servlet > <servlet-name > servlet1</servlet-name > <servlet-class > c.f.s.MyServlet1</servlet-class > </servlet > <servlet-mapping > <servlet-name > servlet1</servlet-name > <url-parttern > *.do</url-parttern > </servlet-mapping >
1 MVC和三层架构
通过Login案例,一个LoginServlet中
接收请求
完成JDBC操作
根据结果做出响应
以上这种开发模式,不好,不便于后期迭代维护
在开发中有一个思想:”分而治之”
MVC思想
M model/模型
模型主要是指javabean,有一些java类
比如封装数据的类,User类
比如其他功能类,UserService,UserDao
V view/视图
视图就是页面,
比如JSP/HTML
为了展现数据
C controller/控制器
三层架构: 是指开发中编码时项目结构,主要是指将不同的功能代码再细分
2 Login案例
需求: HTML页面中输入框用户名和密码,登录
登录成功给出提供,欢迎
登录不成功,给出提示,用户名或密码错误
2.1 搭建环境 数据库环境
1 2 3 4 5 6 7 8 9 10 CREATE TABLE `tb_user` ( `id` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '用户编号' , `username` varchar (10 ) DEFAULT NULL COMMENT '用户名' , `password` varchar (10 ) DEFAULT NULL COMMENT '密码' , `phone` varchar (11 ) DEFAULT NULL COMMENT '手机号' , `createTime` date DEFAULT NULL COMMENT '注册时间' , `money` double (10 ,2 ) DEFAULT NULL COMMENT '账户余额' , `sex` int (1 ) DEFAULT NULL COMMENT '性别 1男2女' , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 24 DEFAULT CHARSET= utf8;
项目环境
创建maven-web项目
补全项目结构
导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <dependencies > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.1</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > javax.servlet.jsp-api</artifactId > <version > 2.3.1</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.10</version > </dependency > </dependencies >
项目必备的java包和类
2.2 页面 登录页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <div > <form action ="/day48/login" method ="post" > 用户名<input type ="text" name ="username" > <br > 密码<input type ="password" name ="password" > <br > <input type ="submit" value ="登录" > <br > </form > </div > </body > </html >
2.3 UserServlet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.qf.servlet;import com.qf.model.User;import com.qf.service.UserService;import com.qf.service.impl.UserServiceImpl;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class UserLoginServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8" ); resp.setContentType("text/html;charset=UTF-8" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); UserService userService = new UserServiceImpl ( ); User user = userService.login(username, password); PrintWriter out = resp.getWriter( ); if (user != null ) { out.write("<html>" ); out.write("<body>" ); out.write("<h1>" ); out.write("欢迎" +user.getUsername()+"登录" ); out.write("</h1>" ); out.write("</body>" ); out.write("</html>" ); } else { out.write("<html>" ); out.write("<body>" ); out.write("<h1>" ); out.write("用户名或密码错误!" ); out.write("</h1>" ); out.write("</body>" ); out.write("</html>" ); } } }
2.4 UserService
一般开发时,会将UserService以及UserDao设计成接口+实现类
的形式
可以先设计接口,规定项目的功能
接口还可以松耦合,实现多态,易于代码扩展
UserService接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.qf.service;import com.qf.model.User;import java.util.List;public interface UserService { User login (String username, String password) ; }
UseServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.qf.service.impl;import com.qf.dao.UserDao;import com.qf.dao.impl.UserDaoImpl;import com.qf.model.User;import com.qf.service.UserService;public class UserServiceImpl implements UserService { @Override public User login (String username, String password) { UserDao userDao = new UserDaoImpl (); User user = userDao.login(username, password); return user; } }
2.5 UserDao UserDao接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.qf.dao;import com.qf.model.User;public interface UserDao { User login (String username, String password) ; }
UserDaoImpl实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.qf.dao.impl;import com.qf.dao.UserDao;import com.qf.model.User;import com.qf.util.DBUtil;public class UserDaoImpl implements UserDao { @Override public User login (String username, String password) { String sql = "select * from tb_user where username = ? and password = ?" ; User user = DBUtil.selectOne(sql, User.class, username, password); return user; } }
这里使用了DBUtil,不习惯使用的话,也可以使用原始的JDBC自己操作
3 请求转发
请求对象HttpServletRequest
请求的转发(将请求转发到其他的servlet)
跳转页面
请求域(存取数据)
请求转发的总结
请求转发地址栏不动
请求转发是服务器行为,是服务器内部
动作
浏览器只有一次请求
可以当做请求域,数据可以在Servlet之间共享
3.1 请求转发 需求: 发出请求/a 映射AServlet,利用请求转发,将请求转发到BServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.qf.servlet;import javax.servlet.RequestDispatcher;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 AServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("AServlet.doGet..." ); req.getRequestDispatcher("/b" ).forward(req,resp); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.qf.servlet;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 BServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("BServlet.doGet..." ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version ="3.1" > <servlet > <servlet-name > aServlet</servlet-name > <servlet-class > com.qf.servlet.AServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > aServlet</servlet-name > <url-pattern > /a</url-pattern > </servlet-mapping > <servlet > <servlet-name > bServlet</servlet-name > <servlet-class > com.qf.servlet.BServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > bServlet</servlet-name > <url-pattern > /b</url-pattern > </servlet-mapping > </web-app >
3.2 请求域
请求域是指: HttpServletRequest对象相当于是容器,存取数据,可以在请求转发的几个类中共享数据.
存储数据 req.setAttribute(key,value)
取出数据 req.getAttribute(key)
请求域作用以及场景: 在多个Servlet请求转发的时候,用来传递数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.qf.servlet;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Date;public class AServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("AServlet.doGet..." ); req.setAttribute("username" ,"jack" ); req.getRequestDispatcher("/b" ).forward(req,resp); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.qf.servlet;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 BServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("BServlet.doGet..." ); String username = (String) req.getAttribute("username" ); System.out.println("username = " + username); } }
3.3 跳转页面
4 重定向
重定向是HttpServletResponse对象完成一个动作
可以将请求重新跳转至其他Servlet
可以跳转页面
重定向总结:
重定向是浏览器动作
重定向地址栏会有变化
是发出两次请求
请求域中的数据在重定向后不能共享(因为是两次请求)
需求:
需求: 发出请求/a 映射AServlet,利用重定向,将请求重新发送请求
到BServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class AServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("AServlet.doGet..." ); req.setAttribute("username" ,"jack" ); String contextPath = req.getContextPath( ); System.out.println("contextPath = " + contextPath); resp.sendRedirect(contextPath+"/404.html" ); } }
5 注解
JDK1.5后出现的技术,注解(Annotation),是一种注释,给程序注释然后程序运行给JVM中的java代码看的
注解文件既不是类也不是接口
5.1 创建注解文件
5.2 元注解
注解的注解就是元注解
5.2.1 @Target
@Target 目标,用来规定注解能用在什么位置
位置
ElementType
包上
PACKAGE
类/接口/数组/枚举上
TYPE
成员变量/局部变量
FIELD
/LOCAL_VARIABLE
方法/构造方法
METHOD
/CONSTRUCTOR
5.2.2 @Retention
保留,指注解保留到什么时候,或者说叫保留至什么时候生效
–
ps: 如果是自定义注解,一般是为了通过反射技术读取注解,所以要定义保留策略为RUNTIME
保留策略
解释
SOURCE
源码阶段有效
CLASS
编译后class中有效
RUNTIME
运行时生效
5.3 注解参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno { int a () default 0 ; String value () default "" ; String[] values() default "" ; }
注解的参数都是为了通过反射技术去读取到注解参数中的值
5.4 实际应用
Servlet开发中也支持使用注解,大大提高开发效率
@WebServlet 注解,可以取代web.xml中[经典8行]代码
day18 今日内容
0 复习昨日 1 Cookie 2 Session 3 Filter
0 复习昨日
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 MVC和三层架构是什么? MVC 三层架构: controller,service,dao 2 请求转发有什么特点 req.getRequestDispatcher(path).forward(req,resp); 功能: 1) 将请求转发到其他servlet 2) 当做域对象存取数据 req.setAttribute(key,value) req.getAttribute(key) 3) 跳转页面 特点: 1) 地址栏不变 2) 请求转发是服务器动作 3) 一次请求 4) 在一次请求中,请求域是可以共享 3 重定向有什么特点 resp.sendRedirect(location); 功能: 1) 跳转Servlet 2) 跳转页面 特点: 1) 地址栏会改变 2) 重定向是浏览器动作 3) 两次请求 4) 在重定向中,请求域是不可以共享
1 关于路径
关于请求时出的路径
2 IDEA热部署
换句话就是,可以实现修改代码不重启服务器.
设置idea的Tomcat为这样
设置完之后,修改前端页面,只需要鼠标离开代码,idea就会自动更新前端代码
设置完之后,修改后端代码,只需要点击下面地方,就可以服务器中的代码
3 会话技术
javaweb开发中会话就是指客户端与服务器的一次会话交互. 比如使用浏览器访问淘宝,那就是开启会话,关闭浏览器会话结束.
在访问淘宝网站时,经常提醒你登录,登录完成后,访问所有的淘宝的网页,以及功能(下单,查订单,付款,聊天等等…)都不需要登录.
那么,淘宝怎么知道是[你]在操作,或者说淘宝怎么知道是登录了还是没登录呢?
淘宝网站会记录
登录信息
.
记录会话中数据的技术就是会话技术
HTTP协议是无状态,不能保存信息
会话技术是指:
4 Cookie
Cookie是浏览器
技术,用来存储一小段数据.(存储4kb或者8kb)
通过浏览器访问某个网站时,后台服务器创建了Cookie
,通过响应头
返回给浏览器,浏览器就存储了Cookie
,后续每次访问中请求头中都会自动带上cookie访问服务器.
4.1 创建cookie
创建Cookie
Cookie cookie = new Cookie(String key,String value);
创建cookie的构造方法只有一个有参构造
参数都是String类型
默认创建的Cookie,
到期时间是保存至浏览器关闭
保存的路径是当前项目下
通过方法设置
保存时间 setMaxAge(时间值)
保存路径 setPath(String path)
【特别说明:cookie的名字,值,路径一致才是同一个cookie】
4.2 响应给浏览器
resp.addCookie(cookie)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.qf.servlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/ck1") public class TestCookieServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("username" ,"admin" ); cookie.setMaxAge(60 * 60 * 24 * 365 ); resp.addCookie(cookie); } }
4.3 获得Cookie
一旦创建cookie,后续每次请求服务器,请求头中都会自动带上cookie,那么就可以使用req对象获得cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.qf.servlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/ck2") public class TestCookie2Servlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies( ); for (int i = 0 ; cookies != null && i < cookies.length; i++) { Cookie cookie = cookies[i]; String name = cookie.getName( ); System.out.println("name = " + name); String value = cookie.getValue( ); System.out.println("value = " + value); int maxAge = cookie.getMaxAge( ); System.out.println("maxAge = " + maxAge); String path = cookie.getPath( ); System.out.println("path = " + path); } } }
4.4 cookie的路径问题
cookie的路径在创建时默认是当前路径下
其实路径,就是cookie的使用范围
,只有在指定的范围路径内才可以读取的cookie
演示1
第一个Cookie1Servlet,设置2个cookie,不设置路径(默认当前项目名)
创建另外两个Servlet分别取得这个两个cookie
UserServlet,请求路径/user/ck
AdminServlet,请求路径/admin/ck
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebServlet("/create2CK") public class Cookie1Servlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie userCookie = new Cookie ("user" , "user_ck" ); Cookie adminCookie = new Cookie ("admin" , "admin_ck" ); resp.addCookie(userCookie); resp.addCookie(adminCookie); } }
结果: UserServlet和AdminServlet能获得所有的cookie
演示2
第一个Cookie1Servlet,设置2个cookie,设置了不同的路径
另外两个Servlet分别取得这个两个cookie
UserServlet,请求路径/user/ck
AdminServlet,请求路径/admin/ck
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @WebServlet("/create2CK") public class Cookie1Servlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie userCookie = new Cookie ("user" , "user_ck" ); userCookie.setPath(req.getContextPath()+"/user" ); Cookie adminCookie = new Cookie ("admin" , "admin_ck" ); adminCookie.setPath(req.getContextPath()+"/admin" ); resp.addCookie(userCookie); resp.addCookie(adminCookie); } }
结果: UserServlet的请求路径是/day49/user/ck,只获得了/day49/user/下面的cookie
AdminServlet的请求路径是/day49/admin/ck,只获得了/day49/admin/下面的cookie
总结: 因为路径不同,cookie的范围不同,导致cookie取值范围不同.所以为了保证大部分时候都能取出cookie,好多cookie都将路径设置成了/,即所有请求都可以获得到该cookie
4.5 cookie保存中文问题
1 cookie的key就直接不能使用中文
2 value中文会乱码
cookie存储中文时如果乱码可以使用URLEncoder编码,URLDecoder解码
1 2 3 4 5 6 7 8 9 Cookie cookie2 = new Cookie ( URLEncoder.encode("用户名" ,"UTF-8" ),URLEncoder.encode("老王" ,"UTF-8" )); String name = URLDecoder.decode(name1, "UTF-8" );String value = URLDecoder.decode(value1, "UTF-8" );System.out.println(name+"=" +value );
知识点小问
1 2 3 1 cookie是用来干什么的? 2 谁创建,谁存储? 3 如何获取?是谁携带cookie?在哪里携带?
5 Session【重点】
Session也是用于保存会话数据的技术之一。Session是服务器端技术,用于记录用户的状态。
Session是基于Cookie
服务器会为每一个会话创建一个session对象
第一次访问服务器时,会自动创建session,还会创建cookie,并且将sessionid存储到cookie响应到浏览器, key=JSESSIONID , value=22jkhj3451jk345
后续再向服务器发送请求,就会携带cookie,后台服务器就会根据JSESSIONID 找到session,就可以获得其中的数据
5.1 创建session 1 2 3 4 5 HttpSession session = req.getSession( );System.out.println("sessionid = " + session.getId() );
5.2 Session存取数据 1 2 3 4 5 6 7 HttpSession session = req.getSession( );System.out.println("sessionid = " + session.getId() ); session.setAttribute("account" ,"qwer" );
1 2 3 4 5 6 7 @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession( ); String account = (String) session.getAttribute("account" ); System.out.println("Session2Servlet.account = " + account); }
5.3 Session域和Request域
Request域中的数据,只在一次请求中有效
Session域中的数据,一次中会话中有效,无论是请求转发还是重定向
5.4 Session域数据清除 1 2 3 4 5 session.invalidate();
6 记录登录状态案例 需求: 项目中有登录页面(index.html),个人中心页面(info.html),添加用户页面(add.html)
实现目标: 只有登录才可以访问info.html,add.html
实现步骤:
1. **登录成功后将登录信息存入session**
1. **设置拦截器,拦截请求判断有没有登录信息**
1. **有登录信息就放行**
1. **没有登录登录信息就跳转至首页重新登录**
1. **在个人中心有退出登录,会销毁session**
登录成功后将登录信息存入session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); if ("admin" .equals(username) && "123456" .equals(password)) { HttpSession session = req.getSession( ); session.setAttribute("user" ,username); resp.sendRedirect(req.getContextPath()+"/info.html" ); } else { resp.sendRedirect(req.getContextPath()+"/index.html" ); } } }
设置拦截器,拦截请求判断有没有登录信息
有登录信息就放行
没有登录登录信息就跳转至首页重新登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @WebFilter("/*") public class LoginFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String requestURI = req.getRequestURI( ); System.out.println("requestURI = " + requestURI); if (requestURI.contains("info" ) || requestURI.contains("add" )) { HttpSession session = req.getSession( ); Object user = session.getAttribute("user" ); if (user == null ) { resp.sendRedirect(req.getContextPath()+"/index.html" ); return ; } } chain.doFilter(req,response); } @Override public void destroy () { } }
退出登录,销毁session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @WebServlet("/logout") public class LogoutServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession( ); session.removeAttribute("user" ); session.invalidate(); resp.sendRedirect(req.getContextPath()+"/index.html" ); } }
7 拦截器/过滤器(Filter)
拦截请求,可以使用@WebFilter指定拦截的路径
开发步骤
创建类
实现Filter接口
重写方法(init(),doFilter(),destroy())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request,response); }
在使用注解开发中,如果有多个拦截器,顺序是按照拦截器的名字的首字母顺序
应用场景:
拦截请求,判断用户登录
拦截请求,设置编码格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebFilter("/*") public class EncodingFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8" ); response.setCharacterEncoding("utf-8" ); } @Override public void destroy () { } }
day19 今日内容
0 复习昨日 1 session存储登录状态 2 使用拦截器改造 3 Ajax 4 FastJson
0 复习昨日
1 服务器的根路径: 项目名 项目名/login 谁能从服务器发出请求? 请求转发
浏览器的根路径:端口 谁能从浏览器发出请求?如果通过浏览器发请求? a,form,ajax,重定向,手动在地址栏输入请求
2 cookie是谁创建? 后台服务器创建,通过响应返回浏览器,储存在浏览器 由谁携带? 后续每次向该服务器发请求时会在请求头中携带cookie cookie保存时间: -1 是关闭浏览器时销毁 0 直接销毁 具体秒数 指定时间销毁 cookie的路径是一种cookie作用范围,在该路径范围内可以取出cookie
3 session什么时候创建? 当需要使用session当域对象存储数据时,就getSession();获得session
4 session域有什么特点 session域在一次会话中有效,无论请求转发还是重定向都可以取出该数据
5 拦截器如何使用 创建类,实现Filter接口 重写方法init(),doFilter(),destroy() doFilter()执行拦截
// 放行到下一个拦截器 chain.doFilter(req,resp); // 没有拦截器,放行到对应的资源
/login —> LoginServlet,完成登录 /ck1 —> CookieServlet, /index.html —> 页面
1 session存储登录状态
需求: 做到登录认证.
目前,服务器中有关于用户操作的几个Servlet,
UserLoginServlet –> /user/login
UserAddServlet —> /user/add
UserDeleteServlet –> /user/delete
这些Servlet有对应的映射路径,要保证只有登录后才可以
进行注册/删除
实现思路
执行登录时,如果登录成功,将登录信息放入session
在真正执行注册/删除等功能前,先判断有没有登录
其实就是判断session中有没有登录的信息
执行退出时,销毁session
登录,登录成功放入session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebServlet("/user/login") public class UserLoginServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); if ("admin" .equals(username)) { HttpSession session = req.getSession( ); session.setAttribute("username" ,username); System.out.println("UserLoginServlet...登录成功" ); } else { System.out.println("UserLoginServlet...登录不成功,用户名或密码错误" ); } } }
添加用户,执行前判断登录状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebServlet("/user/add") public class UserAddServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession( ); Object username = session.getAttribute("username" ); if (username != null ) { System.out.println("已经登录过,可以执行UserAddServlet...添加用户" ); } else { System.out.println("没有登录,没有权限操作!!" ); } } }
删除用户,执行前判断登录状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @WebServlet("/user/delete") public class UserDeleteServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession( ); Object username = session.getAttribute("username" ); if (username != null ) { System.out.println("已经登录过,可以执行UserDeleteServlet...删除用户" ); } else { System.out.println("没有登录,没有权限操作!!" ); } } }
退出登录,销毁登录状态
1 2 3 4 5 6 7 8 9 10 11 @WebServlet("/user/logout") public class UserLogoutServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession( ); session.invalidate(); System.out.println("退出登录!销毁登录状态!" ); } }
2 使用拦截器改造
场景: 服务器中有很多处理请求的servlet,项目要求有登录认证.所以我们每个Servlet执行功能前都要对登录信息判断,那就会有很多次登录判断的代码编写,重复性很高!
因为以上操作不高效,代码重复率太高! 解决方案: 拦截器
使用拦截器,拦截请求,对请求进行判断,如果有登录信息就放行
没有登录信息就响应到首页,提示
改造后面的Servlet,不再进行判断session
1 2 3 4 5 6 7 8 @WebServlet("/user/add") public class UserAddServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("已经登录过,可以执行UserAddServlet...添加用户" ); } }
添加拦截器,执行拦截
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.qf.session;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.io.IOException;@WebFilter("/user/*") public class LoginFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException {} @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String uri = req.getRequestURI( ); if (uri.contains("/login" )) { chain.doFilter(request,response); return ; } HttpSession session = req.getSession( ); Object username = session.getAttribute("username" ); if (username != null ) { chain.doFilter(request,response); } else { System.out.println("没有登录,不能操作,没有权限!!" ); } } @Override public void destroy () {} }
LoginServlet,LogoutServlet和之前一样,不用改动
3 Ajax 3.1 介绍
Ajax即A synchronous J avascript A nd X ML(异步JavaScript和XML )
使用Ajax技术网页应用能够快速地将增量更新呈现在用户界面 上,而不需要重载(刷新)整个页面
,这使得程序能够更快地回应用户的操作。
3.2 语法
使用JavaScript原生操作ajax很麻烦,我们一般使用JQuery封装好的ajax操作,很简单
(jquery对象等价于$)
1 2 3 4 5 6 7 $.get (url,[data],[function ],[type]);
1 2 3 4 5 6 7 $.post (url,[data],[function ],[type]);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $.ajax ({ url :"路径" , type :"GET" , data :"" |{}, contentType : "application/x-www-form-urlencoded" , dataType : "json" , success :function (ret ){ }, error : function ( ){ } });
3.3 测试 3.3.1 $.get 需求: 分别使用$.get , $.post , $.ajax
发送请求,后台servlet接收请求,并做出响应
第一步: 项目得引用jquery.js
第二步: 写页面,页面中使用ajax发请求
1 2 3 4 5 6 7 8 <button onclick ="getRequest()" > 发送$.get请求</button > <br > <br > <br > <script src ="/jquery-2.1.0.js" > </script > <script > function getRequest ( ){ $.get ("http://localhost:8080/ajax/req" ,{username :"gegege" },function (ret ){ console .log ("ret" ,ret) }); }
第三步: 后台接收请求,并响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @WebServlet("/ajax/req") public class AjaxGetServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); System.out.println("$.get发送的数据: username = " + username ); resp.setCharacterEncoding("UTF-8" ); PrintWriter out = resp.getWriter( ); out.write("这是后台的响应~~~" ); } }
第四步: 将数据响应给回调函数
3.3.2 $.post 前端发请求
1 2 3 4 5 6 function postRequest ( ){ $.post ("/ajax/req" ,{password :"12345" },function (ret ){ console .log (ret.code ) console .log (ret.msg ) }) }
后台接收请求并响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebServlet("/ajax/req") public class AjaxReqServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String password = req.getParameter("password" ); System.out.println("$.post发送的数据: password = " + password ); resp.setContentType("application/json;charset=utf-8" ); PrintWriter out = resp.getWriter( ); out.write("{\"code\":200,\"msg\":\"POST请求成功\"}" ); } }
3.3.3 $.ajax 前端发请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function ajaxRequest ( ){ $.ajax ({ url :"/ajax/req2" , data :{username :"root" ,password : "ROOT" }, type :"POST" , success :function (ret ){ if (ret.code == 200 ) { alert (ret.msg ); } }, error :function ( ){ alert ("服务器正忙,请稍后再试~" ) } }) }
后端接收请求,并作出响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @WebServlet("/ajax/req2") public class AjaxReq2Servlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); System.out.println("$.ajax-GET发送的数据: username = " + username+",password = " + password ); resp.setContentType("application/json;charset=utf-8" ); PrintWriter out = resp.getWriter( ); out.write("{\"code\":200,\"msg\":\"请求成功\"}" ); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); System.out.println("$.ajax-POST发送的数据: username = " + username+",password = " + password ); System.out.println(1 /0 ); resp.setContentType("application/json;charset=utf-8" ); PrintWriter out = resp.getWriter( ); out.write("{\"code\":200,\"msg\":\"请求成功\"}" ); } }
ps: 以上doGet和doPost代码是一模一样,其实可以省略这么写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @WebServlet("/ajax/req2") public class AjaxReq2Servlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); System.out.println("$.ajax-POST发送的数据: username = " + username+",password = " + password ); System.out.println(1 /0 ); resp.setContentType("application/json;charset=utf-8" ); PrintWriter out = resp.getWriter( ); out.write("{\"code\":200,\"msg\":\"请求成功\"}" ); } }
3.4 响应json问题 使用方式1: 后台响应json字符串
,前端需要解析
为json对象再使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @WebServlet("/ajax/get") public class AjaxGetServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); System.out.println("$.get发送的数据: username = " + username ); resp.setCharacterEncoding("UTF-8" ); PrintWriter out = resp.getWriter( ); out.write("{\"code\":200,\"msg\":\"请求成功\"}" ); } }
1 2 3 4 5 6 7 8 $.get ("http://localhost:8080/ajax/get" ,{username :"gegege" },function (ret ){ var json = JSON .parse (ret); console .log (json) console .log (json.code ) console .log (json.msg ) }); }
使用方式2: 后台设置响应内容为json
,前端直接使用json对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @WebServlet("/ajax/get") public class AjaxGetServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); System.out.println("$.get发送的数据: username = " + username ); resp.setContentType("application/json;charset=utf-8" ); PrintWriter out = resp.getWriter( ); out.write("{\"code\":200,\"msg\":\"请求成功\"}" ); } }
1 2 3 4 5 6 7 8 $.get ("http://localhost:8080/ajax/get" ,{username :"gegege" },function (ret ){ console .log ("ret" ,ret) console .log (typeof ret) console .log (ret.code ); console .log (ret.msg ); }); }
3.5 BUG: JS引入失败 因为服务器部署时没有将js文件加载,
4 案例 4.1 注册 需求: 注册时,写完用户名,提示该用户名是否存在可否注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 思路: 1 前端页面,输入框 2 当失去焦点时触发函数 3 函数内发出ajax请求,要携带输入框的数据到服务 7 回调函数接收到服务器的数据 根据状态码做出对应的dom操作 后端 4 接收请求,接收请求中的数据 5 service --> dao ---> jdbc 6 返回数据,如果数据库查到数据,说明已经注册过,响应500 如果数据库没有查到,说明没有注册过,响应200 数据库 别忘了加入mysql驱动,数据库连接池 model+servlet+service(impl)+dao(impl)
4.2 回显数据 需求: 展现数据时,在输入完用户id后,下方的个人信息内容直接自动补全
1 2 3 4 5 6 7 8 9 10 11 整个思路: 通过id查用户,返回一个用户对象,在前端展现 前端 1 页面表格,很多个输入框 2 输入完id,触发失去焦点函数 3 发送ajax请求,将id发送服务器 6 回调函数接收服务器返回的数据,操作dom补全表格数据 后端 4 接收请求,接收数据 servlet --> service --> dao 5 将查询的结果响应回ajax
5 FastJson
以后工作中前后端数据的交互都是以JSON形式
.
1 2 3 4 5 { code: 200 , msg: "OK" , data: { } }
再封装一个java对象,对应与json格式
1 2 3 4 5 6 7 8 public class ResultData { private int code; private String msg; private Object data; }
目前使用手动拼接json字符串,很麻烦,不好用!!
FastJson专业也用于转换JSON(阿里巴巴开源)
字符串转json
对象转json
集合转json
json转字符串,对象,集合,数组等
项目中引入依赖
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.46</version > </dependency >
在需要转json的地方使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.qf.ajax;import com.alibaba.fastjson.JSON;import com.qf.model.User;import com.qf.service.UserService;import com.qf.service.impl.UserSericeImpl;import com.qf.util.ResultData;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;@WebServlet("/user") public class UserFindServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String idStr = req.getParameter("id" ); int id = Integer.parseInt(idStr); UserService userService = new UserSericeImpl (); User user = userService.findUserById(id); ResultData resultData = new ResultData ( ); resultData.setCode(200 ); resultData.setMsg("OK" ); resultData.setData(user); String jsonStr = JSON.toJSONString(resultData); resp.setContentType("application/json;charset=utf-8" ); PrintWriter out = resp.getWriter( ); out.write(jsonStr); } }
day20 今日内容
0复习
复习
这一阶段: javaweb,学习写项目
数据库
前端: 展现数据和收集数据
展现数据
数据何时加载
情况1: 页面加载时去查数据
情况2: 点击某些按钮(a标签,form表单)
其他情况
通过什么手段查数据
核心思想就是通过http请求
a标签,href=”路径”
form,action=”路径”
$.get,$.post,$.ajax
其中的url=”路径”
ifram src=”路径”
数据又如何展现
前后端交互使用json.后端返回json
从json中把数据取出,通过操作dom,展现数据
收集数据
后端
maven : 项目管理工具
管理项目结构
1 2 3 4 5 6 7 8 9 |-项目名 |---src |------main |---------java |---------resources |---------webapp |------test |---pom.xml |---target
管理项目依赖
tomcat
服务器,运行web项目的容器
idea关联tomcat
部署项目到tomcat
运行
访问(按照tomcat关联和部署时候的路径)
访问出现404
检查tomcat路径是否正确
检查访问页面是不是不存在
检查访问请求是不是没有对应的url-partten映射路径
检查部署target中是不是没生效
清空idea缓存重启idea
重新打包部署
servlet
servlet是可以运行在服务器上的一个程序
servlet主要作用: 接收请求,做出响应
前端发送get请求,重写doGet(),前端发送post请求,重写doPost()
使用request对象接收请求
使用response对象做出响应
关于request
可以接收请求数据 req.getParameter(“name”)
可以当做域对象,存取数据(在一次请求转发的时候,请求域中的数据可以共享)
请求转发(服务器的内部动作,可以将一个请求转发到另外 一个servlet),还可以转发到页面
关于response
可以向浏览器响应一些内容
PrinterWriter out = resp.getWriter()
out.writer(“xxx”);
一般用于响应json
重定向可以将一个请求重新到另外 一个servlet),还可以重新跳转到页面
resp.sendRedirect()
路径会变,浏览器动作,是两次请求
写项目的架子: mvc思想和三层架构
servlet : 控制层,跟前端交互,控制接收请求,调用业务层处理业务,做出响应
service: 处理业务逻辑
dao : 处理数据库
要复习
框架标签
dom操作(获得dom,dom取值赋值)
js/jq函数+事件
前端F12调试工具
1 2 3 4 5 text("<span style='color:red'>sss</span>") 添加的内容全部是字符串 html("<span style='color:red'>sss</span>") 如果添加的有标签时,会解析成对应的标签效果 text(),html()它是给开闭标签之间设置内容,如果之前有内容会覆盖 ------ append("<span style='color:red'>sss</span>"),在开闭标签之间内容的后面追加,且会解析为标签效果
day21 今日内容
0 复习昨日
1 layui
0 引言
我们已经学完
Java基础
web服务器(tomcat+servlet)
数据库,jdbc
html,css,js,jq
项目管理工具maven
以上这些技术,已经可以写项目,写B/S项目
写页面太慢了,有点丑
所以要使用现成的,提供好的,前端组件或模块 —> 拿来即用
Layui 1 介绍
layui(谐音:类 UI) 是一套开源的Web UI解决方案,采用自身经典的模块化
规范,并遵循原生HTML/CSS/JS的开发方式,常适合网页界面的快速开发
。layui 区别于那些基于MVVM 底层的前端框架,它更多是面向后端开发者
,无需涉足前端各种工具,只需面对浏览器本身,让一切所需要的元素与交互。
2021年9月,layui 官网发布公告称,layui 官网 2021 年 10 月 13 日 进行下线,届时,包括新版下载、文档和示例在内的所有框架日常维护工作,将全部迁移到 Github 和 Gitee。
虽然没有官网,但是”网友”都自己又重新部署了一些网站,内容是layui一模一样
Layui - 经典开源模块化前端 UI 框架 (winxapp.cn)
Layui - 经典开源模块化前端 UI 组件库 (layuion.com)
通过官网得知,layui框架的主要内容是[页面元素],[内置模块]两部分
页面元素: 理解为一些静态html+css样式组件,(拿走即用)
内置模块
layui 定义了一套更轻量的模块规范
将一些特殊功能,比如一些动态效果,封装成了一个一个的函数,一个功能就是一个模块,每个模块有对应的名字
用的时候需要加载这些函数或者说模块,然后再使用对应的功能
2 环境搭建 2.1 下载
Layui - 经典开源模块化前端 UI 组件库 (layuion.com)
2.2 解压
将上一步下载的压缩包,解压得到如下结构文件
1 2 3 4 5 6 |-layui |---css |------modules |------layui.css // 核心样式文件 |---font |---layui.js // 核心js库
2.3 项目搭建 开发前端页面,工具可以使用HBuilder,VSCode,IDEA.
为了配合后面写项目,今天就使用IDEA来开发
2.3.1 创建javaweb项目
创建maven-web项目
暂时不用导入依赖(因为不写java代码)
配置tomcat,部署项目
2.3.2 导入layui资源 静态资源(html/css/js/各种图片)放在项目webapp下
2.3.3 页面中引入layui
只需要在页面中引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <script src ="/layui/layui.js" > </script > </body > </html >
3 入门使用
演示: 使用模块弹出层,使用页面样式button
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <button type ="button" class ="layui-btn" > 一个标准的按钮</button > <a href ="" class ="layui-btn" > 一个可跳转的按钮</a > <script src ="/layui/layui.js" > </script > <script > layui.use (['layer' ], function ( ){ var layer = layui.layer ; layer.msg ('Hello World - layui' ); }); </script > </body > </html >
4 页面元素
页面元素: 理解为一些静态html+css样式组件,(拿走即用)
4.1 布局
layui有栅格系统,采用业界比较常见的 12 等分规则
布局容器
class=”layui-container” 水平居中,两边有留白
class=”layui-fluid” 铺满全屏
栅格系统要配合容器使用,先定义容器,容器中定义行,行内定义列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 布局</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <div class ="layui-container" style ="background-color: red;height: 300px" > <div class ="layui-row" style ="background-color: #00f7de;height: 200px;" > <div class ="layui-col-md9" style ="background-color: #01aaed" > 9/12 </div > <div class ="layui-col-md3" style ="background-color: #009688" > 3/12 </div > </div > </div > <hr > <div class ="layui-fluid" style ="background-color: red;height: 300px" > <div class ="layui-row" style ="background-color: #00f7de;height: 200px;" > <div class ="layui-col-md9" style ="background-color: #01aaed" > 9/12 </div > <div class ="layui-col-md3" style ="background-color: #009688" > 3/12 </div > </div > </div > <hr > <div class ="layui-container" style ="height: 300px" > <div class ="layui-row layui-col-space10" style ="height: 150px;" > <div class ="layui-col-md4 layui-col-md-offset4" style ="background-color: #009688;height: 150px;" > 1/3 </div > </div > <div class ="layui-row" style ="background-color: #00f7de;height: 150px;" > <div class ="layui-col-md8" style ="background-color: #01aaed;height: 150px;" > 2/3 </div > <div class ="layui-col-md4" style ="background-color: #009688;height: 150px;" > 1/3 </div > </div > </div > <script src ="/layui/layui.js" > </script > </body > </html >
4.2 图标
通过对一个内联元素(一般推荐用 i 标签)设定 *class=”layui-icon”*,来定义一个图标,然后对元素加上图标对应的 font-class ,即可显示出你想要的图标,譬如:
1 2 3 4 5 6 <div style ="border: #01aaed 2px solid;height: 300px;width: 300px" > <i class ="layui-icon layui-icon-face-smile" style ="font-size: 30px; color: #FF5722;" > </i > <br > <i class ="layui-icon layui-icon-heart-fill" style ="font-size: 60px; color: #009688;" > </i > </div >
4.3 按钮
向任意HTML元素
设定*class=”layui-btn”,建立一个基础按钮。通过追加格式
为 layui-btn-{type}*的class来定义其它按钮风格。
1 2 3 4 5 6 7 8 <button class ="layui-btn" > 按钮</button > <a class ="layui-btn" > a标签,链接</a > <i class ="layui-btn" > i标签</i > <button class ="layui-btn layui-btn-primary" > 按钮-原始</button > <button class ="layui-btn layui-btn-normal" > 按钮-normal</button > <button class ="layui-btn layui-btn-warm" > 按钮-warm</button > <button class ="layui-btn layui-btn-danger" > 按钮-danger</button > <button class ="layui-btn layui-btn-disabled" > 按钮-layui-btn-disabled</button >
4.4 表单
在一个容器中设定 class=”layui-form” 来标识一个表单元素块,通过规范好的HTML结构及CSS类,来组装成各式各样的表单元素,并通过内置的 form模块
来完成各种交互。
依赖加载模块:form (请注意:如果不加载form模块,select、checkbox、radio等将无法显示,并且无法使用form相关功能)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 表单</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <div class ="layui-container" > <div class ="layui-row" > <div class ="layui-col-md6 layui-col-md-offset3" > <form class ="layui-form" action ="" > <div class ="layui-form-item" > <label class ="layui-form-label" > 输入框</label > <div class ="layui-input-block" > <input type ="text" name ="username" required lay-verify ="required" placeholder ="请输入标题" autocomplete ="off" class ="layui-input" > </div > </div > <div class ="layui-form-item" > <label class ="layui-form-label" > 密码框</label > <div class ="layui-input-inline" > <input type ="password" name ="password" required lay-verify ="required" placeholder ="请输入密码" autocomplete ="off" class ="layui-input" > </div > <div class ="layui-form-mid layui-word-aux" > 辅助文字</div > </div > <div class ="layui-form-item" > <label class ="layui-form-label" > 选择框</label > <div class ="layui-input-block" > <select name ="city" lay-verify ="required" > <option value ="" > </option > <option value ="0" > 北京</option > <option value ="1" > 上海</option > <option value ="2" > 广州</option > <option value ="3" > 深圳</option > <option value ="4" > 杭州</option > </select > </div > </div > <div class ="layui-form-item" > <label class ="layui-form-label" > 复选框</label > <div class ="layui-input-block" > <input type ="checkbox" name ="like[write]" title ="写作" > <input type ="checkbox" name ="like[read]" title ="阅读" checked > <input type ="checkbox" name ="like[dai]" title ="发呆" > </div > </div > <div class ="layui-form-item" > <label class ="layui-form-label" > 开关</label > <div class ="layui-input-block" > <input type ="checkbox" name ="switch" lay-skin ="switch" > </div > </div > <div class ="layui-form-item" > <label class ="layui-form-label" > 单选框</label > <div class ="layui-input-block" > <input type ="radio" name ="sex" value ="男" title ="男" > <input type ="radio" name ="sex" value ="女" title ="女" checked > </div > </div > <div class ="layui-form-item layui-form-text" > <label class ="layui-form-label" > 文本域</label > <div class ="layui-input-block" > <textarea name ="desc" placeholder ="请输入内容" class ="layui-textarea" > </textarea > </div > </div > <div class ="layui-form-item" > <div class ="layui-input-block" > <button class ="layui-btn" lay-submit lay-filter ="formDemo" > 立即提交</button > <button type ="reset" class ="layui-btn layui-btn-primary" > 重置</button > </div > </div > </form > </div > </div > </div > <script src ="/layui/layui.js" > </script > <script > layui.use ('form' ,function ( ){ var form = layui.form ; }) </script > </body > </html >
4.5 导航
导航一般指页面引导性频道集合,多以菜单的形式呈现,可应用于头部和侧边,是整个网页画龙点晴般的存在。面包屑结构简单,支持自定义分隔符。
千万不要忘了加载 element
模块。虽然大部分行为都是在加载完该模块后自动完成的,但一些交互操作,如呼出二级菜单等,需借助element模块才能使用
通过对导航追加CSS背景类,让导航呈现不同的主题色
1 2 3 4 //如定义一个墨绿背景色的导航 <ul class ="layui-nav layui-bg-green" lay-filter ="" > … </ul >
layui-bg-cyan(藏青)、 layui-bg-molv(墨绿)、 layui-bg-blue*(艳蓝)
水平、垂直、侧边三个导航的HTML结构是完全一样的,不同的是:
垂直导航需要追加class:layui-nav-tree 侧边导航需要追加class:layui-nav-tree layui-nav-side
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 导航</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <ul class ="layui-nav layui-bg-green layui-nav-tree layui-nav-side" lay-filter ="" > <li class ="layui-nav-item" > <a href ="" > 最新活动</a > </li > <li class ="layui-nav-item layui-this" > <a href ="" > 产品</a > </li > <li class ="layui-nav-item" > <a href ="" > 大数据</a > </li > <li class ="layui-nav-item" > <a href ="javascript:;" > 解决方案</a > <dl class ="layui-nav-child" > <dd > <a href ="" > 移动模块</a > </dd > <dd > <a href ="" > 后台模版</a > </dd > <dd > <a href ="" > 电商平台</a > </dd > </dl > </li > <li class ="layui-nav-item" > <a href ="" > 社区</a > </li > </ul > <script src ="/layui/layui.js" > </script > <script > layui.use ('element' ,function ( ){ var element = layui.element ; }) </script > </body > </html >
4.6 表格
现在使用的是table的样式,是静态效果,要想有动态效果,比如加载数据,表格排序需要使用table
模块
代码: 看文档
5 内置模块
内置模块
layui 定义了一套更轻量的模块规范
将一些特殊功能,比如一些动态效果,封装成了一个一个的函数,一个功能就是一个模块,每个模块有对应的名字
用的时候需要加载这些函数或者说模块,然后再使用对应的功能
使用模块的语法
要先加载模块,layui 通过 use 方法加载模块
再获得对应的模块对象
在use方法的回调中完成业务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script > layui.use (['form' ,'layer' ,'table' ],function ( ){ var form = layui.form ; var layer = layui.layer ; var table = layui.table ; form.on ('submit(formDemo)' ,function ( ){ return false ; }) layer.msg (); layer.confirm (); layer.alert (); table.render () }); </script >
5.1 弹出层
弹出层,模块名layer
弹出层方法
layer.open(options)
layer.msg(content,options,end)
layer.alert(content,options,end)
layer.confirm(content,options,yes,end)
layer.confirm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <script src ="/layui/layui.js" > </script > <script > layui.use ('layer' ,function ( ){ var layer = layui.layer ; layer.confirm ("是否删除?" ,{ area : ['500px' , '300px' ], title :"删除???" , skin :"layui-layer-molv" , shade : [0.5 , '#393D49' ], shadeClose :false },function (index ){ console .log ("确定删除" ) layer.close (index); },function (index ){ console .log ("取消删除" ) layer.close (index); }); }) </script >
layer.open(options)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 layer.open ({ type :0 , content :"这是一个open弹框" , title :"open" , area :['500px' , '300px' ], yes :function (index ){ console .log ("确定..." ) layer.close (index) }, cancel :function (index ){ console .log ("取消..." ) layer.close (index) } })
layer.open(options) 弹出一个页面,例如添加页面
1 2 3 4 5 layer.open ({ type :2 , content :"http://localhost:8080/02bd.html" , area :['500px' ,'600px' ] })
5.2 日期
模块名: laydate
写一个input输入框,设置一个id
加载模块,获得对象laydate
使用laydate对象,渲染
日期框效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <body > <input id ="test" > <script src ="/layui/layui.js" > </script > <script > layui.use ('laydate' ,function ( ){ var laydate = layui.laydate ; laydate.render ({ elem :"#test" , type :"datetime" , range :false , format :"yyyy年MM月dd日" }) }) </script > </body >
5.3 表格
模块加载名称:table
用于在表格中对数据进行一系列动态化的操作
使用步骤
写一个空table标签,设置id
加载table模块,获得table对象
开始渲染
5.3.1 入门演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 表格</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <div class ="layui-container" > <div class ="layui-row" > <div class ="layui-col-md6 layui-col-md-offset3" > <table id ="test" > </table > </div > </div > </div > <script src ="/layui/layui.js" > </script > <script > layui.use ("table" ,function ( ){ var table = layui.table ; table.render ({ elem :"#test" , height :312 , url :"https://www.fastmock.site/mock/4aa2a624e009051fe726c20c2419c09b/layuidata/api/users" , page :true , cols :[[ {field :"id" ,title :"编号" ,width :90 ,sort :true }, {field :"username" ,title :"用户名" }, {field :"sex" ,title :"性别" }, {field :"sign" ,title :"签名" }, {field :"experience" ,title :"经验" } ]] }) }) </script > </body > </html >
5.3.2 后台servlet接口
ps: 后台接口,指的是前后端交互对接的口子,接口.也就是指后端控制层代码.
需求: 前端使用layui.table模块,加载后台数据并在前端展现.
后台代码编写,就是三层架构(servlet+service+dao),返回一个json数据
三层架构的代码不再粘贴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ResultData { private int code; private String msg; private Object data; public static ResultData fail () { ResultData resultData = new ResultData ( ); resultData.setCode(500 ); resultData.setMsg("失败" ); return resultData; } public static ResultData ok () { ResultData resultData = new ResultData ( ); resultData.setCode(200 ); resultData.setMsg("成功" ); return resultData; } public static ResultData ok (Object data) { ResultData resultData = new ResultData ( ); resultData.setCode(200 ); resultData.setMsg("成功" ); resultData.setData(data); return resultData; } }
5.3.3 转换返回的数据格式 table 组件默认规定的数据格式为:
1 2 3 4 5 6 { "code" : 0 , "msg" : "" , "count" : 1000 , "data" : [ { } , { } ] }
接口返回的数据格式并不一定都符合 table 默认规定的格式,比如我们自己的返回值
1 2 3 4 5 { "code" : 200 , "msg" : "" , "data" : [ { } , { } ] }
那么需要借助 parseData 回调函数将其解析成 table 组件所规定的数据格式
1 2 3 4 5 6 7 8 9 10 11 12 13 table.render ({ elem : '#demp' ,url : '' ,parseData : function (res ){ return { "code" : res.code == 200 ? 0 : -1 , "msg" : res.msg , "count" : res.data .length , "data" : res.data }; } });
5.3.4 数据模板
table模板内,有一个参数templet,可以将后台返回的数据再转换成指定的数据返回给table
templet 函数,有一个参数 d(包含当前行数据及特定的额外字段)。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 table.render ({ elem :"#test" , height :312 , url :"http://localhost:8080/user/list" , page :true , cols :[[ {field :"id" ,title :"编号" ,width :90 ,sort :true }, {field :"username" ,title :"用户名" ,align :"center" }, {field :"password" ,title :"密码" }, {field :"phone" ,title :"手机号" }, {field :"createTime" ,title :"注册时间" ,templet :function (d ){ let now = new Date (d.createTime ); let year = now.getFullYear (); let month = now.getMonth () +1 ; let day = now.getDate (); return year+"年" +month+"月" +day+"日" ; }}, {field :"money" ,title :"余额" ,sort : true }, {field :"sex" ,title :"性别" ,templet :function (d ){ return d.sex == 1 ? '男' :'女' ; }} ]],
5.3.5 分页数据
table.render中设置 page:true,开启分页
page参数还可以设置为laypage模块中的参数分页组件文档 - Layui (layuion.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 table.render ({ elem :"#test" , height :312 , url :"http://localhost:8080/user/list" , page :{ count : 2000 , limit : 2 , limits :[2 ,4 ,6 ,8 ,10 ], first :"首页" , last :"尾页" } }
table.render在渲染数据时,发送请求时会自动拼接出分页参数
?page=1&limit=10 意思是指,从第一页展现,每页展现10条
5.3.6 操作按钮
layui.table提供两种方案来给表格提供操作按钮
通过toolbar,指定按钮脚本
通过templet,自己拼接
方案一:toolbar
1)设置按钮脚本
2)使用toolbar引用脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <script type ="text/html" id ="btnScript" > <a class ="layui-btn layui-btn-xs layui-btn-warm" > 编辑</a > <a class ="layui-btn layui-btn-xs layui-btn-danger" > 删除</a > </script > <script type ="text/javascript" src ="/layui/layui.js" > </script > <script type ="text/javascript" > layui.use ("table" ,function ( ){ var table = layui.table ; table.render ({ elem :"#test" , height :312 , url :"http://localhost:8080/user/list" , cols :[[ { title :"操作" ,toolbar :"#btnScript" } ]], }) }) </script >
方案二: templet自己拼接
1 2 3 4 5 6 7 { title :"操作" ,templet :function (d ){ let str = "<a class=\"layui-btn layui-btn-xs layui-btn-warm\">编辑</a>" ; str += " <a class=\"layui-btn layui-btn-xs layui-btn-danger\">删除</a>" ; return str; } }
5.3.7 操作按钮事件
layui有自己的事件处理方式
需要注意的!!!!
table.on(‘tool(myTableFilter)’
tool是固定的事件名,不能乱改
myTableFilter是table标签中filter的名字
需要使用ajax发送请求的话,
1)项目中需要加入jquery.js文件
2)文件中需要使用script引用jquery文件
3)layui加载jquery模块
4)获得jquery对象就可以操作了
day22 今日内容
0 复习昨日
1 layui-CRUD练习
0 复习昨日
1 开发流程
需求分析
数据库
技术
servlet+jdbc
mysql
html+css+js+jq+ajax+layui
工具
2 搭建环境 2.1 创建项目
2.2 导入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <dependencies > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.1</version > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > javax.servlet.jsp-api</artifactId > <version > 2.3.1</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.46</version > </dependency > </dependencies >
2.3 配置文件 jdbc.properties,放在main/resources下
1 2 3 4 5 6 7 8 9 10 11 12 13 driverClass =com.mysql.jdbc.Driver url =jdbc:mysql://localhost:3306/java2217?useSSL=false username =root password =123456 initialSize =10 maxActive =50 minIdle =5 maxWait =5000
2.4 包结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |-src |---main |------java |---------com.qf |---------com.qf.model |---------com.qf.servlet |---------com.qf.service |---------com.qf.service.impl |---------com.qf.dao |---------com.qf.dao.impl |---------com.qf.util |---------com.qf.filter |------resources |------webapp -pom.xml
2.5 实体类,工具类 1 2 3 4 5 6 7 8 9 10 11 public class User { private int id; private String username; private String password; private String phone; private Date createTime; private double money; private int sex; }
DBUtil
ResultData (符和前后端交互使用的json格式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package com.qf.util;public class ResultData { private int code; private String msg; private int count; private Object data; public ResultData () { } public static ResultData ok (Object data) { ResultData resultData = new ResultData ( ); resultData.setCode(0 ); resultData.setMsg("成功" ); resultData.setData(data); return resultData; } public static ResultData ok (Object data,int count) { ResultData resultData = new ResultData ( ); resultData.setCode(0 ); resultData.setMsg("成功" ); resultData.setData(data); resultData.setCount(count); return resultData; } public static ResultData fail () { ResultData resultData = new ResultData ( ); resultData.setCode(-1 ); resultData.setMsg("失败" ); return resultData; } public int getCode () { return code; } public void setCode (int code) { this .code = code; } public String getMsg () { return msg; } public void setMsg (String msg) { this .msg = msg; } public int getCount () { return count; } public void setCount (int count) { this .count = count; } public Object getData () { return data; } public void setData (Object data) { this .data = data; } }
2.6 拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.qf.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter("/*") public class EncodingContentTypeFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException {} @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8" ); response.setContentType("application/json;charset=UTF-8" ); chain.doFilter(request,response); } @Override public void destroy () {} }
2.7 前端环境
3 功能开发 3.1 首页
需求: 打开index.html展现所有用户数据
前端页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 首页</title > <link href ="/layui/css/layui.css" rel ="stylesheet" > </head > <body > <div class ="layui-container" > <div class ="layui-row" > <div class ="layui-col-md3 layui-col-md-offset5" > <h1 > 用户管理系统</h1 > </div > </div > <div class ="layui-row" > <div class ="layui-col-md10 layui-col-md-offset1" > <table id ="userTable" > </table > </div > </div > </div > <script src ="/layui/layui.js" > </script > <script > layui.use (["table" ],function ( ){ let table = layui.table ; table.render ({ elem :"#userTable" , url :"/user/list" , cols :[[ {field : "id" , title : "ID" , sort : true ,width :80 }, {field : "username" , title : "用户名" ,width :100 }, {field : "password" , title : "密码" ,width :100 }, {field : "phone" , title : "手机号" ,width :120 }, {field : "money" , title : "余额" , sort : true ,width :100 }, {field : "sex" , title : "性别" , sort : true ,width :80 ,templet :function (obj ){ return obj.sex == 1 ? '男' :'女' ; }}, {field : "createTime" , title : "注册时间" , sort : true ,templet :function (obj ) { let date = new Date (obj.createTime ); let year = date.getFullYear (); let month = date.getMonth () + 1 ; if (month < 10 ) { month = "0" +month; } let day = date.getDate (); if (day < 10 ) { day = "0" +day; } return year+"-" +month+"-" +day; }}, ]] }) }) </script > </body > </html >
后端代码
UserListServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.qf.servlet;import com.alibaba.fastjson.JSON;import com.qf.service.UserService;import com.qf.service.impl.UserServiceImpl;import com.qf.util.ResultData;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/user/list") public class UserListServlet extends HttpServlet { private UserService userService = new UserServiceImpl (); @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ResultData resultData = userService.findAll(); String jsonString = JSON.toJSONString(resultData); resp.getWriter().write(jsonString); } }
UserService&UserServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface UserService { ResultData findAll () ; } ----- public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl (); @Override public ResultData findAll () { List<User> list = userDao.findAll(); return ResultData.ok(list); } }
UserDao&UserDaoImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface UserDao { List<User> findAll () ; } ---------------------------------------- public class UserDaoImpl implements UserDao { @Override public List<User> findAll () { List<User> list = DBUtil.selectAll("select * from tb_user" , User.class); return list; } }
3.2 添加 前端
设置添加按钮
绑定js事件,弹出层
弹出层内加载user-add.html
user-add.html里面写表单样式
绑定提交按钮,点击提交触发ajax事件 ,发送请求及数据
在user-add.html关闭弹出层
在index.html中等弹出层销毁后,再重新加载数据
后端
接收数据,完成处理(servlet+service+dao)
作出响应
3.3 操作按钮 1 2 3 4 5 6 7 { title :"操作" ,templet :function (d ){ let str = "<a class=\"layui-btn layui-btn-xs layui-btn-warm\">编辑</a>" ; str += " <a class=\"layui-btn layui-btn-xs layui-btn-danger\">删除</a>" ; return str; } }
3.4 删除 前端
点击删除按钮,弹出确认框
给table设置lay-filter,并且给两个操作按钮设置lay-event
给表格绑定工具栏事件 table.on(“tool(过滤器)”,function(obj){})
通过event判断具体事件
layer弹出确认框
点击取消,不删除,关闭弹出层
点击确定,使用ajax发送请求,携带当前行的id
,执行删除
关闭弹出层,重新加载表格数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 table.on ("tool(userTableFilter)" ,function (obj ) { if (obj.event == "delete" ) { layer.confirm ("确定删除吗?" ,{icon : 3 , title :'提示' },function (index ){ $.get ("/user/delete" ,{id :obj.data .id },function (ret ) { if (ret.code == 0 ) { layer.msg ("删除成功!" ) } else { layer.msg ("删除失败!" ) } }); layer.close (index); table.reload ("userTable" ) },function (index ){ layer.msg ("取消删除!" ) layer.close (index); }) }else if (obj.event == "edit" ) { } });
后端
接收请求,接收id
servlet <–> service <–> dao
响应
3.5 更新 3.5.1 更新弹出层 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 else if (obj.event == "edit" ) { layer.open ({ type :2 , content :"user-edit.html" , area : ["400px" , "500px" ], title :"更新用户" , end :function ( ){ table.reload ("userTable" ) } }) }
后续还要改动上面代码,现在只是先弹出更新层
3.5.2 更新页面回显数据 弹出层出现后,在表单内回显数据
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 else if (obj.event == "edit" ) { layer.open ({ type :2 , content :"user-edit.html" , area : ["400px" , "500px" ], title :"更新用户" , success :function (layero, index ){ let body = layer.getChildFrame ("body" , index); let userData = obj.data ; body.find ("input[name=username]" ).val (userData.username ) body.find ("input[name=password]" ).val (userData.password ) body.find ("input[name=phone]" ).val (userData.phone ) body.find ("input[name=money]" ).val (userData.money ) let date = new Date (userData.createTime ); let year = date.getFullYear (); let month = date.getMonth () + 1 ; if (month < 10 ) { month = "0" +month; } let day = date.getDate (); if (day < 10 ) { day = "0" +day; } let createTime = year+"-" +month+"-" +day; body.find ("input[name=createTime]" ).val (createTime) body.find ('input[name=sex][value=1]' ).attr ('checked' ,userData.sex == 1 ? true : false ); body.find ('input[name=sex][value=2]' ).attr ('checked' ,userData.sex == 2 ? true : false ); }, end :function ( ){ table.reload ("userTable" ) } }) }
user-edit.html
1 2 3 4 5 6 7 8 9 10 11 12 layui.use (["form" ,"laydate" ,"jquery" ,"layer" ],function ( ){ let form = layui.form ; let laydate = layui.laydate ; let $ = layui.jquery ; let layer = layui.layer ; setTimeout (function ( ){ form.render (); },50 ) });
3.5.3 执行更新 user-edit.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 form.on ("submit(formDemo)" ,function (obj ) { $.ajax ({ url :"/user/edit" , type :"POST" , data :obj.field , success :function (ret ) { if (ret.code == 0 ) { var index = parent.layer .getFrameIndex (window .name ); parent.layer .close (index); } else { layer.msg ("添加失败" ) } }, error :function ( ){ layer.msg ("服务器正忙!" ) } }) return false ; })
后台
servlet–service-dao
3.6 分页 前端
table.render在渲染数据时,设置page:true,开启分页发送
请求时会自动拼接出分页参数?page=1&limit=10
意思是指,从第一页展现,每页展现10条
也可以通过设置limit:n,来设置每页多少条数据
1 2 limit :3 ,limits :[3 ,6 ,9 ,12 ],
后端
1 2 3 4 5 6 7 1 修改后台UserListServlet中代码,接收分页数据 2 修改后台UserService和UserServiceImpl的findAll(),添加参数int pageNo,int pageSize 3 修改后台UserDao和UserDaoImpl的findAll(),添加参数int pageNo,int pageSize -------- 发现分页的页数不对,需要后台查询数据条数返回给前端
数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 select * from tb_user limit 0 , 3 select * from tb_user limit 3 , 3 select * from tb_user limit 6 , 3 select * from tb_user limit (pageNo-1 )* pageSize, pageSize
3.7 模糊查询 前端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <div class ="layui-row" > <div class ="layui-col-md9 layui-col-md-offset2" > <form class ="layui-form layui-form-pane" action ="" > <div class ="layui-form-item" > <div class ="layui-input-inline" > <select name ="field" > <option value ="" > ---请选择---</option > <option value ="username" > 用户名</option > <option value ="phone" > 手机号</option > </select > </div > <div class ="layui-input-inline" > <input type ="text" name ="keyword" placeholder ="请输入搜索关键词" class ="layui-input" > </div > <div class ="layui-input-inline" > <button class ="layui-btn layui-btn-normal" lay-submit lay-filter ="search" > 搜索</button > <button class ="layui-btn layui-btn-warm" lay-submit lay-filter ="findAll" > 查询全部</button > </div > </div > </form > </div > </div >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 <script > var table; var $ ; var layer; var form; layui.use (["table" ,"jquery" ,"layer" ,"form" ],function ( ){ table = layui.table ; $ = layui.jquery ; layer = layui.layer ; form = layui.form ; form.on ("submit(search)" ,function (obj ) { console .log (obj) let field = obj.field .field ; let keyword = obj.field .keyword ; renderTable (field,keyword); return false ; }) form.on ("submit(findAll)" ,function (obj ) { renderTable (); return false ; }) renderTable (); }) function renderTable (field,keyword ) { let url = "http://localhost:8080/user/list" if (field != undefined && keyword != undefined && field != '' && keyword != '' ) { url += "?field=" +field+"&keyword=" +keyword; } table.render ({ elem :"#userTable" , url :url, page :true , limit :3 , limits :[3 ,6 ,9 ,12 ], cols :[[ {field : "id" , title : "ID" , sort : true ,width :80 }, {field : "username" , title : "用户名" ,width :100 }, {field : "password" , title : "密码" ,width :100 }, {field : "phone" , title : "手机号" ,width :120 }, {field : "money" , title : "余额" , sort : true ,width :100 }, {field : "sex" , title : "性别" , sort : true ,width :80 ,templet :function (obj ){ return obj.sex == 1 ? '男' :'女' ; }}, {field : "createTime" , title : "注册时间" , sort : true ,templet :function (obj ) { let date = new Date (obj.createTime ); let year = date.getFullYear (); let month = date.getMonth () + 1 ; if (month < 10 ) { month = "0" +month; } let day = date.getDate (); if (day < 10 ) { day = "0" +day; } return year+"-" +month+"-" +day; }}, { title :"操作" ,templet :function (d ){ let str = "<a lay-event=\"edit\" class=\"layui-btn layui-btn-xs layui-btn-warm\">编辑</a>" ; str += " <a lay-event=\"delete\"class=\"layui-btn layui-btn-xs layui-btn-danger\">删除</a>" ; return str; } } ]] }) } </script >
后端
1 2 3 4 5 6 主要是改动findAll()方法,传入pageNo,pageSize,field,keyword参数 所以设计了map,承载所有参数 ----- Dao层,要动态拼接sql (要判断)
数据库
1 select * from tb_user where ??? like '%???%' limit x,y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 select * from tb_user where phone like '%xxx%' limit x,yselect * from tb_user where 'phone' like '%1%' limit 0 ,3 select * from tb_user limit 0 ,3 select * from tb_user where username like '%邱%' limit 0 , 3 select * from tb_user where phone like '%222%' limit 0 , 3 select * from tb_user limit 0 , 3 select count (* ) count from tb_userselect count (* ) count from tb_user where phone like '%222%'
day23 今日内容
0 复习昨日
1 了解项目开发流程
2 项目需求
3 设计数据库
4 编码
0 复习昨日
1 jdbc 五大步骤 1)加载驱动 2)获得连接 3)获得执行sql对象 4)执行sql 5)关流
2 Statement使用方式 自己写完整的sql语句,如果有参数,都要自己拼接sql 使用statement调用方法executeQuery(sql),executeUpdate(sql)
3 PreparedStatement使用方式 自己写sql语句,如果有参数,用?占位 conn.preparedStatement(sql)进行预处理 对?赋值 ps.executeUpdate(),executeQuery()
4 它俩区别 处理语句,需要自己写完整sql(需要拼接),容易sql注入 预处理语句,sql不需要拼接,避免sql注入
5 写出分页sql语句,假设每页5条,第三页的数据 select * from 表名 limit (pageNo - 1) * pageSize,pageSize select * from 表名 limit 10,5
6 画出后台代码的执行流程(三层架构中的数据流转)
1 了解项目开发流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 1.公司部门的组成 人事部门HR 技术部门(研发部/IT部/java组/h5组/c组/ui组/产品) 行政部门 财务部门 市场部门 运营部门 总经理 老板/董事/CEO 2.项目部人员的组成 各种开发人员: UI/前端/后端(java/c/Python/c++/android/Object-c) 测试/实施/运维 3.项目的来源(怎么接项目) 客户(人脉/广告效应/朋友推荐/...) 4.可行性方法分析 5.=======立项======== 6.报价表以及项目周期 7.合同签订 (首付30%,中期(30%),后期(30%),交付使用,(10%)) 8.人员分配 中等项目(2月) 1个UI 1个前端 4个后台(1Android 1IOS 2Java) 1个测试 9.=======项目开发======= 10.原型图(甲方/乙方),思维导图 11.需求文档-接口文档 12.技术选型,架构设计,数据库设计(*) 几张表 表中多少字段 字段约束 表和表之间的关系 ,产品设计 13.项目编码,单元测试 14.测试(测试环境 公司测试,内部测试) 15.产品使用说明书【专业】 16.上线,项目部署
2 项目需求 详见文档
3 数据库设计 设计一个独立的库: apartment2217
建表(一个模块一张表)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 DROP TABLE IF EXISTS `activity`;CREATE TABLE `activity` ( `id` int (11 ) NOT NULL COMMENT '活动id' , `time ` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '活动时间' , `subject` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '活动主题' , `intr` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '活动内容介绍' , `address` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '活动地址' , `price` double (10 ,2 ) DEFAULT NULL COMMENT '活动费用' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '活动表' ; INSERT INTO `activity` VALUES ('1' , '2022-11-20 17:19:21' , '相亲' , '单身青年相亲' , '同乐公园' , '100.00' );DROP TABLE IF EXISTS `contract`;CREATE TABLE `contract` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `num` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '合同号' , `hid` int (11 ) DEFAULT NULL COMMENT '关联房屋id' , `lid` int (11 ) DEFAULT NULL COMMENT '关联租户id' , `time ` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '合同签订时间' , `startTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '合同生效起始时间' , `endTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '合同失效时间' , `totalMoney` double (255 ,0 ) DEFAULT NULL COMMENT '合同总金额' , `payType` int (11 ) DEFAULT NULL COMMENT '1月付 2半年付 3年付' , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 2 DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '合同表' ; INSERT INTO `contract` VALUES ('1' , '2022112001' , '1' , '1' , '2022-11-20 16:49:00' , '2022-11-21 16:49:02' , '2022-11-22 16:49:05' , '180' , '1' );DROP TABLE IF EXISTS `house`;CREATE TABLE `house` ( `id` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '主键' , `address` varchar (255 ) COLLATE utf8_bin NOT NULL COMMENT '房屋地址' , `floor` int (11 ) COLLATE utf8_bin DEFAULT NULL COMMENT '楼层' , `roomNum` int (11 ) DEFAULT NULL COMMENT '房间号' , `area` varchar (50 ) COLLATE utf8_bin DEFAULT NULL COMMENT '面积' , `dir` varchar (50 ) COLLATE utf8_bin DEFAULT NULL COMMENT '朝向' , `deco` int (11 ) DEFAULT NULL COMMENT '装修类型 1毛坯 2精装' , `air` int (11 ) DEFAULT NULL COMMENT '是否双气 1是 2否' , `price` double (10 ,2 ) DEFAULT NULL COMMENT '价格' , `rentStatus` int (11 ) DEFAULT NULL COMMENT '出租状态 1已出租 2未出租 3停止出租' , `image` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '房屋图片路径' , `addTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '添加时间' , `updateTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' , `status` int (11 ) DEFAULT NULL COMMENT '房屋状态 1正常 2已删除' , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 6 DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '房屋表' ; INSERT INTO `house` VALUES ('1' , '老代庄10号' , 1 , '102' , '30平' , '南' , '1' , '1' , '1980.00' , '2' , null , '2022-11-23 10:46:08' , '2022-11-23 00:00:00' ,1 );INSERT INTO `house` VALUES ('2' , '航海路60号' , 2 , '209' , '65平' , '西' , '2' , '2' , '2000.00' , '3' , null , '2022-11-23 10:48:44' , '2022-11-23 00:00:00' ,1 );INSERT INTO `house` VALUES ('3' , '二七万达' , 1 , '3' , '56平' , '东' , '3' , '1' , '13000.00' , '1' , null , '2022-11-25 16:13:16' , '2022-11-25 16:13:16' ,1 );INSERT INTO `house` VALUES ('4' , '海为科技园' , 1 , '3' , '200平' , '北' , '1' , '2' , '300.00' , '3' , null , '2022-11-25 16:13:13' , '2022-11-25 16:13:13' ,1 );INSERT INTO `house` VALUES ('5' , '台胞小区' , 1 , '8' , '90平' , '东北' , '2' , '1' , '40.00' , '1' , null , '2022-11-25 16:13:12' , '2022-11-25 16:13:12' ,1 );DROP TABLE IF EXISTS `lessee`;CREATE TABLE `lessee` ( `id` int (11 ) NOT NULL AUTO_INCREMENT COMMENT '主键ID' , `name` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '租户姓名' , `tel` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '手机号' , `sex` int (11 ) DEFAULT NULL COMMENT '性别 1男 2女' , `np` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '籍贯' , `idCard` varchar (255 ) COLLATE utf8_bin NOT NULL COMMENT '身份证号码' , `addTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 2 DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '租户表' ; INSERT INTO `lessee` VALUES ('1' , '张三' , '110' , '1' , '郑州' , '4101010110101' , '2022-11-20 16:38:15' );DROP TABLE IF EXISTS `logi`;CREATE TABLE `logi` ( `id` int (11 ) NOT NULL COMMENT '主键ID' , `name` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '后勤人员姓名' , `idCard` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '后勤人员身份证号' , `tel` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '后勤人员手机' , `sex` int (11 ) DEFAULT NULL COMMENT '1男 2女' , `addTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP , `salary` double (10 ,2 ) DEFAULT NULL COMMENT '工资' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '后勤人员表' ; DROP TABLE IF EXISTS `maintain`;CREATE TABLE `maintain` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `hid` int (11 ) DEFAULT NULL COMMENT '关联房屋id' , `loid` int (11 ) DEFAULT NULL COMMENT '关联后勤人员id' , `time ` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '维修时间' , `result ` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '维修结果' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '维修表' ; DROP TABLE IF EXISTS `rent`;CREATE TABLE `rent` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `hid` int (11 ) DEFAULT NULL COMMENT '关联房屋id' , `lid` int (11 ) DEFAULT NULL COMMENT '关联租户id' , `price` double (10 ,2 ) DEFAULT NULL COMMENT '缴纳房租' , `payTime` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '房租表' ; INSERT INTO `rent` VALUES ('1' , '1' , '1' , '180.00' , '2022-11-20 16:56:02' );DROP TABLE IF EXISTS `user `;CREATE TABLE `user ` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `username` varchar (255 ) COLLATE utf8_bin DEFAULT NULL , `password` varchar (255 ) COLLATE utf8_bin DEFAULT NULL , `sex` int (11 ) COLLATE utf8_bin DEFAULT NULL COMMENT '1男 2女' , `realname` varchar (255 ) COLLATE utf8_bin DEFAULT NULL COMMENT '真实姓名' , `hiredate` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '入职时间' , `status` int (11 ) DEFAULT NULL COMMENT '1正常 2离职' , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 4 DEFAULT CHARSET= utf8 COLLATE = utf8_bin COMMENT= '用户表' ; INSERT INTO `user ` VALUES ('1' , 'admin' , '123456' , 1 ,'邱世举' , '2022-11-22 16:22:54' , '1' );INSERT INTO `user ` VALUES ('2' , 'zs' , '123456' , 2 ,'张三' , '2022-11-22 16:23:12' , '1' );INSERT INTO `user ` VALUES ('3' , 'ls' , '123456' ,2 , '李四' , '2022-11-22 16:23:30' , '1' );
4 编码 4.1 前端脚手架
可以快速搭建一个前端界面,
X-admin(目前使用这个)
Layui mini
这两个脚手架都是基于Layui改造的
如果使用?
看上哪个效果,就赋值粘贴哪个页面,根据实际需求改动页面内容
4.2 环境搭建
创建项目
导入依赖
创建对应包结构
创建对应实体类
工具类,配置文件
加入前端静态资源
在webapp下,创建static文件夹
把前端脚手架资源拷贝到static下
另外单独导入jquery.js
配置tomcat
部署项目
4.3 登录页
在webapp下创建login.html
复制xadmin脚手架的代码
改动css.js的引入路径
web.xml设置欢迎页
4.4 登录功能 前端
登录表单提交事件
获得用户名+密码数据
发ajax请求,到后台
ajax的回调函数中判断是否登录成功
登录失败,弹框提示
登录成功,跳转页面到首页
后端
后台servlet接收请求 <–> service <–> dao
如果查询到,要存储登录状态(session)
响应
4.5 首页样式跳转 前端
登录成功后跳转首页
4.6 展现房屋数据
登录成功,跳转至首页后,立即展现房屋列表
是通过iframe的src指定路径,会自动发请求加载list页面
list页面,通过layui-table渲染数据
5 模糊搜索 要实现的是多条件搜索,关键词和字段有多套(比如,地址字段对应地址的内容|楼层字段,对应楼层内容….)
使用json优化渲染表格的方法参数
使用table.render字段参数where,优化拼接路径
后台要根据关键词动态拼接sql
6 多表联查 业务需求: 查询合同时,需要关联查询房屋和租户,
1 select c.* ,h.address,l.name from contract c, house h,lessee l where c.hid = h.id and c.lid = l.id
因为查询到的数据,不止有合同信息,还要关联房屋,租户信息,如何封装查询到数据?此时一个单独的合同类不能够展现全部数据,怎么解决?
实体类中展现的数据不够,就加?
加哪? 一般不直接加在原实体类,还是创建一个扩展类ContractVO
还有另外的一种思路:”分而治之”
分开一个一个查
先查询且只查询合同
根据上一步查询得到的结果房屋id,租户id
根据hid查房屋
根据lid查租户
7 下拉框回显 复选框/单选框选中是添加 checked
下拉框选中是添加 selected
8 批量删除 9 js-cookie 登录成功后,主页右上角人名应该是当前登录的人.
是login.html登录成功,跳转到index.html
需要在index.html展现登录成功的人名
人名数据在哪?人名在login.html登录成功的回调函数中
那么,数据如何从login.html传递到index.html?
其中一个方案是cookie,注意是前端js操作cookie
在login.html将数据存储到cookie
在index.html从cookie中取出
操作dom显示数据
官方文档JavaScript Cookies (w3school.com.cn)