视频-22:
-
在没有Spring之前,我们MD5加密是通过单独的UTIL包;后面慢慢演变为java自身的MD5包;在Spring来临之后,企业都是使用spring的MD5加密;shiro来临之后,企业开始使用shiro的反复加密加盐;MD5被中国人破解,山东大学的教授。
-
UUID算法的使用,UUID被java封装了一个类叫UUID,可以直接拿来加密:
String uuid = UUID.randomUUID().toString().replace("-", ""); //将UUID生成的随机吗转换为字符,并且去掉其中的-,这是常用的写法。
UUID最大的特点是超大数据量不会重复,所以企业中常用在主键中。
-
JAVA的时间戳:
System.currentTimeMillis(): //时间戳,避免冲突 //多用户并发 请求时间戳 + 用户的IP + 服务器响应时间戳+数据长度值 = 特殊时间戳
-
timestamp:年-月-日-时–分-秒-毫秒 ,java中有这个类,Mysql中也有这个类型的字段。
-
SQL中常用的字段属性:
CREATE TABLE `yonghu` 创建一个表,表名字为yonghu NOT NULL 字段不能为空 DEFAULT 字段值为默认值 CURRENT_TIMESTAMP 获取当前系统的时间 COMMENT sql中的注释,数据库中的初始化时间是1970-00-00 00:00:00:000 PRIMARY KEY 主键 UNIQUE KEY 唯一键(不能重复) ENGINE=InnoDB 数据库内核引擎(InnoDB是企业中主要使用的内核) DEFAULT CHARSET=utf8 设置语言编码(注意:切忌不要写成utf-8)
视频23,24:
-
数据库数据量小的时候,可以使用数据表的外键,因为关联性比较强;而当数据量大的时候,使外键反而是一种负担,数据量越大外键的性能越低。
-
怎样将本表的数据插入到本表中?使用纯SQL。这个很重要,在做测试的时候很重要。SQL单表的数据量级别:5W 10W 50W 100W
-
逆向工程:mybatis-generator-maven-plugin。注意的是,逆向工程不会检查是否存在重复的文件,会直接覆盖!!
-
BigDecimal,JAVA的大数类。
-
Mybatis逆向工程配置文件:
-
修改数据库的物理路径:
<!-- 本地数据库驱动程序jar包的全路径 --> <classPathEntry location="/home/yg/文档/dlycrm/web/WEB-INF/lib/mysql-connector-java-5.1.6.jar"/>
-
指定mybatis版本库`既可:
<!-- 指定mybatis版本 --> <context id="context" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressAllComments" value="false"/> <property name="suppressDate" value="true"/> </commentGenerator>
-
修改数据库的相关配置,
<driverClass>
不同的版本数据库驱动不一样,connectionURL
修改为自己的数据库路径,userId
数据库用户名,password
用户数据库密码。<!-- 数据库的相关配置 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/dlycrm" userId="root" password="Yg_20001008"/>
-
<javaTypeResolver>
保持默认:<!--指定生成的类型为java类型,避免数据库中number等类型字段 --> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver>
-
<javaModelGenerator>
生成所有的POJO对象,其中属性targetPackage设置POJO对象保存的package,targetProject设置生成的POJO包的路径,本地使用src,maven使用src/main/java:<!-- POJO实体类生成的位置 修改targetPackage和targetProject 生成POJO实体类时将会直接覆盖没目标包所指向的路径,因此我们一般那指向一个测试的包路径,而不会直接只想项目的pojo使用的包路径下--> <javaModelGenerator targetPackage="com.gxaedu.crm.test" targetProject="src"> <property name="enableSubPackages" value="false"/> <property name="trimStrings" value="true"/> </javaModelGenerator>
-
<sqlMapGenerator>
生成所有的mapper.xml文件,其中属性的意义和上述相同:<!-- *Mapper.xml 文件的位置 修改targetPackage和targetProject--> <sqlMapGenerator targetPackage="com.gxaedu.crm.test" targetProject="src"> <property name="enableSubPackages" value="false"/> </sqlMapGenerator>
-
<javaClientGenerator>
生成所有的mapper接口,其属性作用和上述一致:<!-- Mapper 接口文件的位置 修改targetPackage和targetProject--> <javaClientGenerator targetPackage="com.gxaedu.crm.test" targetProject="src" type="XMLMAPPER"> <property name="enableSubPackages" value="false"/> </javaClientGenerator>
-
<table>
标签,代表需要逆向工程的表结构,这个标签按根据需要来决定写多少个,如果需要三个表则要写三个标签。其中该标签的各个属性:tableName
代表数据库表名,domainObjectName
代表POJO类名,enableCountByExample
代表实例模板。<!-- 相关表的配置 多表就配置多个 tableName表名 domainObjectName类名 --> <table tableName="yonghu" domainObjectName="YongHu" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/> <table tableName="bumen" domainObjectName="BuMen" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/> <table tableName="zhiwei" domainObjectName="ZhiWei" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/>
-
-
编写逆向工程Main方法:
package com.gxaedu.crm; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * Created with IntelliJ IDEA. * User: thinknovo * Date: 2020/02/21 * Description: * Version: V1.0 */ public class MybatisGeneratorMain { public static void main(String[] args) throws Exception { List<String> warnings = new ArrayList<>(); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(new File("/home/yg/文档/dlycrm/src/resources/mybatis-generator-config.xml")); // Configuration config = cp.parseConfiguration(ClassLoader.getSystemResourceAsStream("mybatis-generator.xml")); DefaultShellCallback callback = new DefaultShellCallback(true); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); for (String warning : warnings) { System.out.println(warning); } } }
-
Mapper.xml中的个别属性:
- resultType:指向当前工程中以存在的包名+类名
- resultMap配置一个自定义的格式map
- 企业中不一定POJO的属性名就是数据库表的字段名,当遇到这种情况时,使用resultType返回内容会失败,这时不能进行反射。
<resultMap id="BaseResultMap" type="com.gxaedu.crm.test.BuMen"> <!-- type指向项目中的POJO类 --> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. --> <!-- id代表主键 column代表字段名 jdbcType代表数据库字段类型 prooerty代表pojo属性名 result代表的是普通字段 resultMap保存的就是一种POJO的属性到Mysql字段的映射关系--> <id column="id" jdbcType="VARCHAR" property="id" /> <result column="bumenmingcheng" jdbcType="VARCHAR" property="bumenmingcheng" /> <!-- jdbcType=DATE对应的java的是java.util.Date 目前企业中很少使用java.util.Date包 而是使用java.sql包中Date(年月日)、Time(十分秒)、Timestamp(年月日十分秒毫秒)--> <result column="chenglishijian" jdbcType="DATE" property="chenglishijian" /> <result column="bumenmiaoshu" jdbcType="VARCHAR" property="bumenmiaoshu" /> <!-- 需要注意在Mybatis中下划线是myBaites的一个关键字,因此Mysql数据中的字段名不能带有下划线,否则在resultMap映射的时候,Mybatis不会在property中加上下划线,就会导致映射失败 --> </resultMap>
- resultMap在单表操作时,使用场景相对偏少, 但是在多表映射操作时,几乎都是resultMap完成。
- 在实际的企业开发场景中,尽可能避免用户的请求的行为是多表操作,因为这样会导致服务器和数据库的压力较大,mysql理论上默认的并发处理能力是1000~10000之间。
- mybatis mapper.xml中的标签,目的是为了解决冗余SQl语句,调用标签通过这种方式调用,很方便的:
<sql id="Base_Column_List"> id, bumenmingcheng, chenglishijian, bumenmiaoshu </sql> <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from bumen where id = #{id,jdbcType=VARCHAR} </select>
-
企业中绝对不能在实际上线版本中,出现select * from table的sql写法,必须显示的把所有字段写出来,这是全球化规范。
-
mybaties中官方建议,所有的sql语句,不要追加分号(因为涉及到后期的动态追加SQL填充),SQL中分号是终止符。
-
parameterType: 这个属性只是提醒,没有直接的约束,严格上讲是可以被删掉的。parameterType可以传递的是java.lang.String,java.lang.Integer ,java.util.Map… 如果传递的是数组,那么可以
通过arg0, arg1, arg2...
;也可以通过param1, param2, param3...
在Mapper中获取。<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from yonghu where id = #{arg0} and mima = #{arg1} //这里是使用方法,这样写就会传入接口的selectByPrimaryKey的两个参数,然后进行填充。 </select>
1.使用arg作为多参数的写法时,需要重arg0开始,从param的写法的话,需要从param1开始。
2.当接口传递进来的参数是一个map时,那么Mapper.xml中应该是使用Map的键取对应的参数值:
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from yonghu where id = #{id} and mima = #{mima} //这里取map中的键,得到参数值 </select>
-
#{id,jdbcType=VARCHAR}
可以简化为#{id}
: -
mybatis官方建议,进行单表操作时不要写别名。
-
<trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="bumenmingcheng != null"> bumenmingcheng, </if> <if test="chenglishijian != null"> chenglishijian, </if> <if test="bumenmiaoshu != null"> bumenmiaoshu, </if> </trim>
mybatis中的标签:、、、、、… 是用于动态组装SQL。
<trim prefix="(" suffix=")" suffixOverrides=",">
其中prefix代表组装的SQL前缀, suffix代表组装的SQL后缀, suffixOverrides代表组装的SQL分割符。
<if test="id != null"> id, </if>
test属性代表判断语句, 如果返回结果为true,则添加if双标签中的SQL语句。
!! 动态SQL最大的价值体现在查询之上。
-
建议去看看MyBatis的官方文档,这是五大常用常见框架都自带的功能。
-
迁移所有的逆向工程内容,迁移过程中注意修改就个特定的文件位置信息,一般直接使用IDEA的自动重构就可以解决。
-
@Resource和@Autowired:
@Resource是java原生的扫描注入, 被Spring包容并支持;
@Autowired是Spring框架的扫描注入。
Spring官方建议使用构造器注入,不建议使用属性级注入。
service、mapper可能会被同时注入多个不同表对应的类。
-
在Spring中,当@serivce没有指定名字的时候,默认的名字就是类名的第一个字母小写的形式userSerivce。
-
在Mybatis中, $代表直接组装SQL,小心有SQL注入的风险; # 代表?预处理形态,java中处理SQL的必备形态。
视频25
- 对接登录:登录流程,一般我们自己考虑的登录接口都很简单,但是实际应用中的登录模块实际上是很复杂的。
- 目前主流的几个关键词:前后分离,读写分离等等。
- 企业标准中,不允许混编写,js、html、css必须是独立文件。
- layui.js的作者是贤心,曾经的阿里大前端架构,强的一批。
- WBE开发中,前端页面修改时,一定要使用chrom的ctrl+shift+R,重置缓存再查看效果。
- sessionStorage状态:
- 当前浏览器有效,关闭浏览器失效;
- 长期有效,清除cookie才会失效,或者到期才会失效。
视频26,27
过滤器1
-
所有的系统中,一般都会判断是否登录,特别是管理系统,必须判断登录状态,只有登录了才可以允许用户进入首页。常用的技术有两种:
- cookie缓存用户的的登录信息;
- session缓存用户的登录信息(这一次我们选用session处理)
本地缓存往往看不到更高级,更深入的内容;服务器缓存赋予用户最高级的功能。
-
filter过滤器(JAVAEE的原声类):用于拦截每一次用户的请求,他的功能特别少,只有三个抽象方法:init()初始化,doFilter(),destroy()销毁。使用这三个方法就可以拦截用户的每一次请求。
- 核心方法是doFilter(过滤).
- filter()跟随服务器启动而启动,跟随服务器关闭而关闭;
- filter也是单实例化的。
- doFiler()方法会传递三个参数:
- ServletRequest servletRequest 请求对象;
- ServletResponse servletResponse 响应对象;
- FilterChain filterChain 过滤链(在过滤链中,还有一个doFilter方法,这个方法是核心,作用是放行)
- 思路理清:Filter类中 -> 核心方法doFilter方法 -> 核心参数FilterChain -> 核心方法doFilter(与前面的doFilter作用不一样,这里的作用是放行)
-
过滤器的实现:
@WebFilter(filterName = "loginFilter", urlPatterns = "/*")
public class LoginFilter implements Filter {
/**
* urlPatterns = "/*" 代表匹配任意路径的请求,即任何路径下的请求都要通过该过滤器的合法性验证
* 一个合法的Filter,必须实现JavaEE中的Filter接口
* Filter接口有三个抽象方法: init() doFilter() destroy()
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
/**
* init()是初始化方法,在Web服务器启动时调用
*/
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
/**
* 用户的每一次请求都会调用,注意是在urlPatterns制定的路径之下的所有访问都会调用该方法
*/
}
@Override
public void destroy() {
/**
* destroy()是销毁方法,在Web服务器关闭时被系统调用
*/
}
}
// 运行机制或者规则,可以理解为制度。
- JAVA中session默认的生存周期是30分钟。可以在web.xml中更改。
<session-config>
<!-- 永不超时 session-timeout单位是分钟 -->
<session-timeout>0</session-timeout>
</session-config>
-
session会缓存在当前浏览器中,session销毁会通过三种形态
- session超时;
- 用户关闭整个浏览器(注意:是整个浏览器);
- 开发人员手动释放(服务器关闭也算释放)
-
因为目前企业开发中,视图层由SpringMVC管理,而Spring团队不建议我们使用原生级别的Filter,因为这样做的话,IOC容器无法管理。
-
SpringMVC拦截实现
- 在spring_mvc.xml添加以下代码
<!--配置拦截器--> <nvc:interceptors> <!-- 拦截器01--> <mvc:interceptor> <!-- springmvc中/*是一个关键符号,所以匹配所有路径时使用/** --> <mvc:mapping path="/**"/> <!-- 定义在<mvc:interceptor>下面的表示匹配制定路径的请求才进行拦截--> <bean class = "com.gxaedu.crm.LoginInterceptor"/> </mvc:interceptor> </nvc:interceptors>
- 写Interceptor代码:
//此刻注意左侧已经有Spring标记了 public class LoginInterceptor implements HandlerInterceptor { public LoginInterceptor() { System.out.println("LoginInterceptor 构造"); } @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { /** * preHandle 方法在用户请求调用之前发; * return代表是否继续执行后面的拦截机制,如果为false,则代表放弃后面所有的拦截;如果为true,则代表继续执行后续的拦截。 */ System.out.println("URL = " + httpServletRequest.getRequestURL()); //用户所访问的绝对路径 System.out.println("URI = " + httpServletRequest.getRequestURI()); //用户所访问的相对路径 System.out.println("RemoteAddr = " + httpServletRequest.getRemoteAddr());//用户请求IP地址 System.out.println("LocalAddr = " + httpServletRequest.getLocalAddr());//用户请求IP地址 HttpSession session = httpServletRequest.getSession(); Object obj = session.getAttribute("username"); String url =httpServletRequest.getRequestURL().toString(); /** * 拦截其会被shiro替代 * 判断用户是否成功登录 * 1.判断用户信息是否在session中 * 2。判断用户是否在login.html中 */ if(url.endsWith("login.html") || url.endsWith(".js") || url.endsWith(".css") || url.endsWith(".ico") || url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".gif") || url.endsWith("/crm_login")){ //如果用户请求的是登录界面则直接放行,如果不放行的话将进入死锁状态。 }else if(obj == null){ if(url.endsWith("login.html")){ }else{ httpServletResponse.sendRedirect("login.html"); } }else{ if(url.endsWith("login.html")){ httpServletResponse.sendRedirect("index.html"); }else{ } } return true; //这里需要为true,代表执行后面的拦截 } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { /** *postHandle方法 该方法会在用户请求调用后,且在返回试图之前执行,此方法可以对请求域中的模型和试图进行 */ } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { /** * afterCompletion方法,该方法会在整个请求完毕之后执行,次方法可以在用户请求完毕之后,释放一些制定资源或者session。 */ } }
-
注意Interceptor和AOP不是同一个内容。
视频28,29
SprincMVC异常处理
- 全局异常处理:(在Spring中不建议使用try catch进行异常处理,官方建议使用Spring的注解)。
@ExceptionHandler
:异常处理方式,当某一个类中有此方法时,此类任何处理报错都会执行次方法内容,而不会再前端抛出500异常。(方法截获)
@ExceptionHandle(Throwable.class)
public String check(){
Sysout.out.println("Faild");
return "login";
}
// 注意,可以自定义异常处理,但是引入的class必须是Throwable的子类
- 在JAVA中调用任何系统级别的方法类时,一定要去查看该方法定义的方法头有没有抛出什么异常,抛出的运行期异常,必须要去处理。
Integer i = Integer.parseInt("a");
//parseInt有一个运行期间的异常,此时如果没有进行处理,则会在运行时,输入非法字符后,程序崩溃。
@ExceptionHandler(Throwable.class)
这种注解,可以放置在任何自定义方法之上,但是此配置只能对当前类有效。因此企业中一般将所有异常写在一个异常包中进行调用,当放入一个异常包中处理的时候,一定要在Spring的配置文件中加上异常包的路径,让他能够被Spring扫描进去。
@ExceptionHandler(Throwable.class)
public Map<String,String> getException(){
Map<String,String> map = new HashMap<>();
map.put("code", "50000");
map.put("msg", "系统错误");
return map;
}
@RestControllerAdvice
与@ControllerAdvice
的区别:他们都是全局捕获异常(也就是可以截获所有@Controller
抛出的异常)。@RestControllerAdvice
是只能返回JSON数据,@ControllerAdvice
是开发人员手动决定返回视图还是数据。- 一行代码实现内存溢出,实现堆栈溢出,实现异常:
Integer i = Integer.parseInt("a"); //会抛出ClassCastException的异常
int[][] array = new int[1000000][1000000]; //将会内存溢出,
//写一个无限递归调用之后将会堆栈溢出:
void test(){
test();
}
-
Timer
与TimerTask
在项目中的应用; -
使用JAVA取出Json中的数据时,如果是花括号就是用
.getJSONObject(index:)
获取,参数是下边,如果是中括号就用.getJSONArray(key:)
获取,参数是JSON对应的键。 -
如果用户发送到本地服务器的请求,需要获取一些第三方服务的内容,我们应该预先写一个后台服务提前获取第三方服务的内容,保存在本地,然后直接返回给用户,而不是用户请求一次然后去调用第三方一次,这样的话延时很大。
-
真假分页:
假分页:用户发送一起请求,后台数据库将返回所有查询数据,前端接受到数据之后,利用js渲染,在本地形成一个分页效果;用户点击下一页的时候不会在发送请求到服务器,只会在本地调用第一次请求传输的数据。
真分页:用户查询某一页的数据,需要发送一次服务器请求,后台服务更具请求页码,在数据库中映射出对应的数据,将这一部分数据传递给前端,然后渲染在HTML上展示给用户。
JAVA中用于分页的工具:PagerUtils
-
部门处理:
- 编写Bumen Controller:
package com.gxaedu.crm.controller.user; import com.gxaedu.crm.pojo.BuMen; import com.gxaedu.crm.service.user.BuMenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.text.ParseException; import java.util.List; import java.util.Map; /** * Created with IntelliJ IDEA. * User: thinknovo * Date: 2020/02/22 * Description: * Version: V1.0 */ @RestController public class BuMenController { @Autowired BuMenService buMenService; /* 插入一条部门数据 */ @RequestMapping(value="/crm_insert_bumen", method = RequestMethod.POST) public Map<String, String> insertBuMen(@RequestBody Map<String, String> map) throws ParseException { System.out.println("map=" + map); return buMenService.insertBuMen(map); } /* 查询所有部门数据 */ @RequestMapping(value="/crm_select_bumen", method = RequestMethod.GET) public List<BuMen> selectBuMen() { System.out.println("BuMenController selectBuMen"); // 直接返回集合框架,Spring MVC会自动转换成json数据格式,此任务由jackson完成,如果项目中没有引入jackson,将执行失败 // 在Spring MVC中,所有时间对象,在转换成json时,都会把时间对象转换完整的毫秒数返回给前端 return buMenService.selectBuMen(); } }
- 编写BuMenService
package com.gxaedu.crm.service.user; import com.gxaedu.crm.pojo.BuMen; import com.gxaedu.crm.mapper.BuMenMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.List; import java.util.Map; import java.util.UUID; /** * Created with IntelliJ IDEA. * User: thinknovo * Date: 2020/02/22 * Description: * Version: V1.0 */ @Service public class BuMenService { @Autowired BuMenMapper buMenMapper; /** * 执行新增部门操作 * @param map 前端传递过来的数据 * @return * @throws ParseException */ @Transactional(rollbackFor = Throwable.class) public Map<String, String> insertBuMen(Map<String, String> map) throws ParseException { // 前端传递过来的部门json数据 // var obj = {"depotId": id, "depotName": depName, "depotSetTime": date, "depotDescribe": describe}; BuMen buMen = new BuMen(); // 启动UUID主键策略 String uuid = UUID.randomUUID().toString().replace("-",""); buMen.setId(uuid); // 部门名称 buMen.setBumenmingcheng(map.get("depotName")); // 部门描述 buMen.setBumenmiaoshu(map.get("depotDescribe")); // 日期转换格式 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); buMen.setChenglishijian(dateFormat.parse(map.get("depotSetTime"))); // 如果插入数据成功,mybatis会返回1,否则会返回0 buMenMapper.insert(buMen); map.put("Status","ok"); map.put("Text","插入数据成功"); map.put("id",uuid); return map; } @Transactional(rollbackFor = Throwable.class) public List<BuMen> selectBuMen() { return buMenMapper.selectBuMen(); } }
- 对应的
BuMenMapper.xml
中添加(还要在接口中添加相应的同名的抽象方法)
<select id="selectBuMen" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from bumen </select>