前言
我将使用MyBits+Tomcat+Severlet开发1个图书管理系统,以熟悉Java Web开发过程。
一、XML
java程序中经常使用XML作为程序的配置文件
1.什么XML?
XML 是Extensible Markup Language 也称为可扩展标记语言, XML的可扩展体现在XML的标签是可以自定义的,而HTML是规定好的。
XML可以存储复杂的数据结构,而properties只能存储变量。
2.XML的功能
存储配置数据
在网络中传输
3.XML和HTML的区别
HTML和XML都是W3c的产物,都可以被浏览器解析。
HTML的标签是预定义的,XML的标签是自定义的。
HTML语法松散,XML语法严格。
HTML侧重于展示数据,XML侧重于存储数据。
4.XML基本语法
- xml的第一行必须定义文档声明
- xml有且只有1个根标签
- xml标签的属性必须使用引号
- xml标签名称区分大小写
<?xml version='1.0'?> <!--xml的第一行必须定义文档声明--> <users> <!--xml有且只有1个根标签--> <user id="1"> <!--属性必须使用引号--> <name>张根</name> <!--xml标签名称区分大小写--> <age>21</age> <gender>Male</gender> </user> <user id="2"> <name>张学友</name> <age>58</age> <gender>Male</gender> </user> <user id="3"> <name>张惠妹</name> <age>43</age> <gender>female</gender> </user> </users>
5.XML约束文档
通常情况下用户(框架的使用者)编写XML文档,框架读取并解析XML文档中定义的配置内容,从而根据用户的定义启动和运行框架。
然而用户可以在XML中定义任意名称的标签,那么框架的作者如何保证框架读取和解析到的内容是框架可识别的有效配置信息,而不是用户随便定义的无效配置信息呢?
这就需要框架的作者提前定义XML约束文档以规范和约束用户可以在XML中定义的配置内容。
在java中约束文档分为以下两类:DTD和Schema格式的约束文档。
1.DTD:简单的XML文档约束技术
<!ELEMENT users (user*)> <!--约束users标签中仅能定义user标签且user标签可以出现*次--> <!ELEMENT user (name,age,gender)> <!--user标签中可以根据顺序出现name,age,gender这3种标签--> <!ELEMENT name (#PCDATA)> <!--约束name标签中的内容为字符串--> <!ELEMENT age (#PCDATA)> <!--约束age标签中的内容为字符串--> <!ELEMENT gender (#PCDATA)> <!--约束gender标签中的内容为字符串--> <!ATTLIST user number ID #REQUIRED> <!--约束user标签有属性,属性名number,属性值且number属性必须-->
-----------------
<?xml version='1.0'?> <!--引入约束文档--> <!DOCTYPE users SYSTEM "users.dtd"> <users> <user number="uid1"> <name>张根</name> <age>21</age> <gender>Male</gender> </user> <user number="uid2"> <name>张学友</name> <age>58</age> <gender>Male</gender> </user> <user number="uid3"> <name>张惠妹</name> <age>43</age> <gender>female</gender> </user> </users>
2.Scheme:一直复杂的XML约束技术
Scheme是一种约束更加严格的约束文档,相较于DTD,Scheme可以约束标签中的内容。
二、Jsoup包
在Python中我们可以使用BeautifulSoup包对XML和HTML进行解析,在Java中我们可以使用Jsoup包完成相同的任务。
在java中我通常使用Jsoup解析XML文档,以下是Jsoup包的功能分解。
Jsoup工具类:解析整个html/xml文档,返回1个Document对象。
Doucment对象:整个HTML/XML文档在内存中的Dom树。
Elements:包含了所有Element对象的集合,当做ArrayList<Element>使用。
Elemen:单个元素对象,封装了所有获取元素的属性、文本内容、标签内容等API。
1.引入Jsoup包
<?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.zhanggen</groupId> <artifactId>maven_jsoup</artifactId> <version>2.0</version> <name>maven_jsoup</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <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> <!-- jsoup HTML parser library @ https://jsoup.org/ --> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.11.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> </build> </project>
2.解析xml文档的方式
我们可以通过DOM和SAX两种方式把xml加载到内容中。
A.DOM方式是将标记语言文档一次性加载进内容,在内存中形成1棵DOM树。
优点:操作方便,可以对各个节点CURD操作
缺点:占用内存
适用于服务器
B.SAX方式是逐行读取加载进内存,基于事件驱动的。
优点:内存占用很少
缺点:不能做进行点CURD操作
适用于手机等内存较小的设备。
3.xml常见的解析器
JAXP:sun公司提供的解析器,支持dom和sax两种思想,但是使用起来比较复杂。
DOM4J:采用DOM的方式解析XML文档
PULL:Android操作系统内置的XML解析器,设计采用sax方式。
Jsoup:Jsoup是一款java的HTML解析器,提供了一套非常好用的API,Jsoup也可以对XML进行解析。
4.Jsoup的基本使用
Jsoup提供了非常简单的API帮助我们解析XML、HTML文档。
A.解析XML文档
package com.zhanggen; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; public class JsoupDemo2 { public static void main(String[] args) throws Exception { //1.获取XML文档的路径 String documentPath = JsoupDemo2.class.getClassLoader().getResource("users.xml").getPath(); //2.解析xml文档进内容,获取xml文档的DOM树,指定文档的字符集 Document document = Jsoup.parse(new File(documentPath), "utf-8"); //3.获取元素对象集合 Elements对象 Elements ageElements = document.getElementsByTag("age"); System.out.println(ageElements.size()); for (int i = 0; i < ageElements.size(); i++) { //4.获取到每个元素:Element对象 Element ageElement = ageElements.get(i); //5.获取每个Element对象中的数据 System.out.println(ageElement.text()); } } }
B.解析HTML文档
package com.zhanggen; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; import java.net.URL; public class JsoupDemo1 { public static void main(String[] args) throws Exception { //1.通过HTML/XML文件路径获取文档 String path = JsoupDemo1.class.getClassLoader().getResource("users.xml").getPath(); //2.通过HTML/XML内容获取文档 String content = "<!DOCTYPE html>\n" + "<html lang=\"en\">\n" + "<head>\n" + " <meta charset=\"UTF-8\">\n" + " <title>Title</title>\n" + "</head>\n" + "<body>\n" + "<h1>您好</h1>\n" + "</body>\n" + "</html> "; //3.通过远程URL地址获取HTML/XML文档 URL url = new URL("https://www.cnblogs.com/sss4/"); Document document = Jsoup.parse(new File(path), "utf-8"); Document document1 = Jsoup.parse(content); System.out.println(document1); Document docment2 = Jsoup.parse(url, 2000); System.out.println(docment2); //获取Dom对象中所有名称为name的标签 Elements elements = document.getElementsByTag("name"); //获取标签的个数 int size = elements.size(); System.out.println(size); //获取第1个名称为name的标签 Element element = elements.get(0); //获取标签的文本内容 String text = element.text(); System.out.println(text); } }
5.Jsoup工具类中各种对象的使用
A.Jsoup工具类
package com.zhanggen; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.File; import java.net.URL; public class JsoupDemo3 { public static void main(String[] args) throws Exception { //1.Jsoup工具类可以通过本地文件路径解析xml/html文件 String path = JsoupDemo3.class.getClassLoader().getResource("users.xml").getPath(); Document document1 = Jsoup.parse(new File(path), "utf-8"); //2.Jsoup工具类可以直接解析字符串内容 String htmlString = "<!DOCTYPE html>\n" + "<html lang=\"en\">\n" + "<head>\n" + " <meta charset=\"UTF-8\">\n" + " <title>张根的个人主页</title>\n" + "</head>\n" + "<body>\n" + "<h1>您好世界!!</h1>\n" + "</body>\n" + "</html>"; Document document2 = Jsoup.parse(htmlString); //3.Jsoup工具类可以通过网络路径发起http请求获取网页数据。 URL Myurl = new URL("https://www.cnblogs.com/sss4/"); Document document3 = Jsoup.parse(Myurl, 3000); System.out.println(document1); System.out.println(document2); System.out.println(document3); } }
B.Document
我们可以根据标签的名称、属性、属性+属性值从document中查询标签。
也可以通过标签的ID获取单个标签。
package com.zhanggen; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; /* * Document对象功能 * * */ public class JsoupDemo4 { public static void main(String[] args) throws Exception { Elements elements = null; String path = JsoupDemo4.class.getClassLoader().getResource("users.xml").getPath(); Document document = Jsoup.parse(new File(path), "utf-8"); //1.根据标签名称查询Elements elements = document.getElementsByTag("user"); System.out.println(elements); //2.根据标签属性名称查询Elements elements = document.getElementsByAttribute("number"); System.out.println(elements); //3.根据标签属性名称和属性值查询Elements elements = document.getElementsByAttributeValue("number", "uid3"); System.out.println(elements); //4.根据标签ID获取单个标签 Element element = document.getElementById("zhangxueyou"); System.out.println(element); } }
三、Project Lombok
在编写Java项目时,尤其想要对类私有字段进行操作时,需要在类中定义大量的set/get方法,即使是IDEAR可以自动生产,也会使代码变得不简洁。
而使用Lombok+注解就可以很优雅地解决代码冗余的问题;
Lombok在javac进行编译的时候,根据代码的注解,对原代码进行代码添加从而产生新的代码被编译。
1.下载Lombok JAR包
首先我们需要去Lombok官网下载它的JAR包。
2.IDEAR下载Lombok插件
3.@AllArgsConstructor
package com.test; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @AllArgsConstructor //生成带所有属性的构造器方法 //Student student = new Student(100, "小根", "男" @NoArgsConstructor(staticName = "create") //Student student1 = Student.create(); public class Student { Integer sid; String name; String sex; }
4.@Getter 和@Setter注解
package com.test; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @Accessors(chain = true) //对象支持链式操作 @Setter //自动生成所有字段的set方法 @Getter //自动生成所有字段的get方法 @AllArgsConstructor //自动生成带所有字段参数的构造方法 new Student(1, "张根", true); class Student { Integer sid; String name; @Getter(AccessLevel.PRIVATE) //设置Pricate属性 boolean sex; }
5.@ToString注解
package com.test; import lombok.*; import lombok.experimental.Accessors; @ToString(includeFieldNames = false, exclude = {"sex"}) @Setter //自动生成所有字段的set方法 @Getter //自动生成所有字段的get方法 @AllArgsConstructor //自动生成带所有字段参数的构造方法 new Student(1, "张根", true); class Student { // @ToString.Include(rank = 1) Integer sid; String name; boolean sex; }
6.@EqualsAndHashCode注解
package com.test; import lombok.*; import lombok.experimental.Accessors; @EqualsAndHashCode //重写Equals和HashCode方法 @ToString @AllArgsConstructor class Student { Integer sid; String name; boolean sex; }
7.@Data
代表设置了@Setter、@Getter、.@EqualsAndHashCode、@ToString所有注解,一旦使用@Data注解就不建议此类有继承关系。
package com.test; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data //代表设置了@Setter、@Getter、.@EqualsAndHashCode、@ToString所有注解 public class Student { Integer sid; String name; String sex; }
8.@Value
@Value注解与@Data注解类似,但是并不会生成@Setter,所有成员属性都是final的。
@Value // public class Student { Integer sid; String name; Strin
9.@SneakyThrows和@Cleanup
package com.test; import lombok.Cleanup; import lombok.SneakyThrows; import java.io.FileInputStream; import java.io.InputStream; public class Main { @SneakyThrows //自动生成TryCatcj语句块 public static void main(String[] args) { @Cleanup //自动调用Close方法 InputStream inputStream = new FileInputStream("lombok.jar"); } }
10.@Builder
package com.test; import lombok.*; @Builder //Student.StudentBuilder student = Student.builder().sid(1).name("小根").sex("男"); public class Student { Integer sid; String name; String sex; }
四、MyBatis
MyBatis是Java中1个半自动化的ORM框架;
1.初识MyBatis
MyBatis配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.0.98:3306/zhanggen"/> <property name="username" value="weike"/> <property name="password" value="weike@123"/> </dataSource> </environment> </environments> <mappers> <mapper url="file:conf/staffMapper.xml"/> </mappers> </configuration>
mapper(映射器)配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="staffMapper1"> <select id="selectStaff" resultType="com.test.entity.Staffs"> select * from staffs; </select> <select id="selectOne" resultType="com.test.entity.Staffs"> select * from staffs where id= #{id}; </select> </mapper>
映射类
package com.test.entity; import lombok.Data; @Data public class Staffs { String username; Integer age; double salary; }
操作数据库
package com.test; import com.sun.xml.internal.ws.api.message.HeaderList; import com.test.entity.Staffs; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.FileInputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Main { public static void main(String[] args) throws Exception { //读取数据库配置文件,创建1个数据连接工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("./conf/mybatis-config.xml")); //打开1个Sessio开启SQL自动提交 try (SqlSession session = sqlSessionFactory.openSession(true);) { //查询列表 List<Staffs> staffList = session.selectList("selectStaff"); staffList.forEach(System.out::println); //查询单条数据 Staffs staff2 = session.selectOne("selectOne", 2); System.out.println(staff2.getUsername()); } } }
2.Mybatis配置文件
注意Mybatis的配置项是有先后顺序的;
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--类型别名可为 Java中定义的类型设置一个缩写名字。--> <typeAliases> <typeAlias alias="Staff" type="com.test.entity.Staffs"/> </typeAliases> <!--默认使用开发环境还是线上环境数据库--> <environments default="development"> <environment id="development"> <!--事务管理使用JDBC--> <transactionManager type="JDBC"/> <!--数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.0.98:3306/zhanggen"/> <property name="username" value="weike"/> <property name="password" value="weike@123"/> <!-- 数据库连接池配置开始 --> <!-- 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10 --> <property name="poolMaximumActiveConnections" value="300"/> <!-- 任意时间可能存在的空闲连接数 默认是5,最好设置为0,否则可能会崩溃掉 --> <property name="poolMaximumIdleConnections" value="0"/> <!-- 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) --> <property name="poolMaximumCheckoutTime" value="20000"/> <!-- 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。 --> <property name="poolTimeToWait" value="20000"/> <!-- 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。 --> <property name="poolPingEnabled" value="true"/> <!-- 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用) --> <property name="poolPingConnectionsNotUsedFor" value="3600000"/> <!-- 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。 --> <property name="poolPingQuery" value="select 1"/> <!-- 数据库连接池配置结束 --> </dataSource> </environment> </environments> <mappers> <!--加载的映射器配置文件--> <mapper resource="com/test/mapper/staffMapper.xml"/> </mappers> </configuration>
3.基本增删改查
映射器配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--通过MyMapper接口把数据记录映射成对象--> <mapper namespace="com.test.mapper.MyMapper"> <!--设置 数据库字段和类属性名 映射关系--> <resultMap id="staffMap1" type="Staff555"> <result column="username" property="username"/> <result column="age" property="age"/> <result column="salary" property="salary"/> </resultMap> <!--resultMap="staffMap1"指向数据字段配置--> <select id="selectStaff" resultMap="staffMap1"> select * from staffs; </select> <select id="selectOne" resultMap="staffMap1"> select * from staffs where id= #{id}; </select> <insert id="addStaff"> insert into staffs(username, age, salary) values (#{username},#{age},#{salary}) </insert> <update id="updateStaff"> UPDATE staffs SET username=#{username},age=#{age},salary=#{salary} WHERE id=#{id}; </update> <delete id="deleteStaff"> delete from staffs where id=#{id}; </delete> </mapper>
映射器配置接口
package com.test.mapper; import com.test.entity.Staffs; import java.util.List; public interface MyMapper { List<Staffs> selectStaff(); Staffs selectOne(Integer id); Integer addStaff(Staffs staff); Integer updateStaff(Staffs staff); Integer deleteStaff(int id); }
映射器配置类
package com.test.entity; import lombok.Data; @Data public class Staffs { Integer id; String username; Integer age; double salary; }
操作数据库
package com.test; import com.sun.xml.internal.ws.api.message.HeaderList; import com.test.entity.Staffs; import com.test.mapper.MyMapper; import org.apache.ibatis.io.ResolverUtil; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.FileInputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Main { public static void main(String[] args) throws Exception { try (SqlSession session = MybatisUtil.getsession(true)) { //1.查询多条记录 MyMapper mapper = session.getMapper(MyMapper.class); mapper.selectStaff().forEach(System.out::println); //2.查询单条记录 System.out.println(mapper.selectOne(2).getUsername()); //3.增加 Staffs newStaff = new Staffs(); newStaff.setUsername("张大千1"); newStaff.setAge(181); newStaff.setSalary(111.1); System.out.println(mapper.addStaff(newStaff)); //4.删除数据 mapper.deleteStaff(27); //5.修改数据 Staffs updateStaff = new Staffs(); updateStaff.setUsername("张大千"); updateStaff.setSalary(122.3); updateStaff.setAge(122); updateStaff.setId(2); mapper.updateStaff(updateStaff); } } }