JavaEE—Java其他一些知识点

1、XML

  • XML(EXtensible Markup Language),可拓展标记语言,以前叫GML

  • XML与HTML的对比

  1. XML主要是来描述数据

  2. HTML主要是来展现数据

  • XML的文档声明若存在则必须放在第一行,描述了XML版本和编码格式

  • 空格和换行在Java中没关系,但是在XML中会被当做内容进行处理,但是现在大部分第三方封装好的解析工具类中会自动忽略

  • xml中会有很多头部文件,其实是一堆协议和名字空间引入。

    • xmlns:xmlNameSpace,即名称空间

1.1、作用

  • 数据传输,不同语言不同机器都可互相传输

  • 配置文件

  • 数据存储,但存储量尽量少些

1.2、元素(标签、节点)

即XML中的标记,也被称为标签、节点

  1. 不能以数字或部分标点符号开头

  2. 不能包含空格和特定的几个符号

  3. 标签必须成对出现,不允许缺省结束标签

  4. 根元素有且只有一个,必须包含根元素,即</>

  5. 大小写敏感

  6. 允许多层嵌套但是不允许交叉嵌套

1.3、属性

  • 属性是写在标签中的信息,属性值用双引号或单引号引起来,遵循外双内单(推荐),也可以是外单内双,多个属性使用 空格 分开,例如使用id进行唯一

  • 现在大多使用 子标签 来描述数据,因为子标签还可以有子标签,更具有层次

1.4、实体

  • 大多数特殊字符不能直接写在XML文件中,比如:<、>、&等

  • 一般使用实体来代替特殊字符,当然也可以自定义

字符实体
<
>
&
'
"
<!DOCTYPE 根元素名称[
  <!ENTITY  实体名  实体内容>
]>
​
<!-- 使用的时候 -->
<name>&company;</name>

1.5、注释

<!-- 这就是注释 -->

1.5.1、使用注释时需注意的点

  1. 注释内容中不要出现 --

  2. 不要把注释放在标签中间

  3. 注释不能嵌套

1.6、CDATA

XML解析器,会解析XML文档中所有的文本,当某个XML元素被解析时,其标签之间的文本也会被解析

  • 解析器进行解析的内容,称为PCDATA(Parsed CDATA)

  • 解析器不会解析的内容,称为CDATA(Character Data)

<![CDATA[在这里放需要原样输出的东西]]>

1.7、XML约束

使用dtd文件或schema文件对XML文件内容进行约束

1.7.1、良构、有效

  • 良构:一个XML文件的内容若满足基本语法要求,那么此XML是良构的

  • 有效:在良构基础上,若此XML文件还通过dtd或schema的约束验证,那么此XML是有效的

1.7.2、DTD

DTD(Document Type Define),dtd文件中描述并规定了元素、属性和其他内容在xml文档中的使用规则,DTD文件的后缀名为 .dtd

1.7.2.1、dtd描述元素(标签)

<!ELEMENT 元素名 内容模式>
  • 内容模式有(4选1):

  1. EMPTY:元素不能包含子元素和文本(即为空元素)

  2. (#PCDATA):可以包含任何字符数据,但是不能在其中包含任何子元素

  3. ANY:元素内容为任意的,主要是使用在元素内容不确定的情况下

  4. 修饰符:() | + * ? , 修饰符可以使用多个,只要不出现冲突

    • (): 用来给元素分用组

    • | :在列出的元素中选择一个

    • +:表示该元素最少出现一次,可以出现多次 (1或n次)

    • *:表示该元素允许出现零次到任意多次(0到n次)

    • ?:表示该元素可以出现,但只能出现一次 (0到1次)

    • ,:对象必须按指定的顺序出现

1.7.2.2、dtd描述属性

<!ATTLIST 元素名 属性名 属性类型 属性特点>
  • 属性类型:

  1. CDATA:属性值可以是任何字符(包括数字和中文)

  2. ID:属性值必须唯一,属性值必须满足xml命名规则,只能是字符

  3. IDREF:属性的值指向文档中其它地方声明的ID类型的值。

  4. IDREFS:同IDREF,但是可以具有由空格分开的多个引用。

  5. (枚举值1|枚举值2|枚举值3...):属性值必须在枚举值中

  • 属性特点:

  1. #REQUIRED:元素的所有示例都必须有该属性

  2. #IMPLIED :该属性可以不出现

  3. default-value:该属性可以不出现,但是会有默认值

  4. #FIXED:属性可以不出现,但是如果出现的话必须是指定的属性值

1.7.3、引入 .dtd 文件

1.7.3.1、内部dtd(不常用)

dtd与xml在同一个文件中,元素名是什么,就代表作用在这个节点的内部

<!DOCTYPE 节点名[
    <!ENTITY company "杰普软件科技有限公司">
    
    <!ELEMENT students (student+)>
]>

1.7.3.2、外部dtd(常用)

  • 本地dtd文件引入,不用些<!DOCUMENT 节点名[这些,只需要些<!ELEMENT 、<!ATTLIST这些就可以了

  • 引用的时候<!DOCTYPE employees SYSTEM "employee.dtd">路径是相对于本文件的包目录下

  • 公共dtd文件引入

<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  • Eclipse默认会根据第一个双引号中的内容,查找对应的DTD所在位置

  • 如果有DTD文件,那么就读取并验证,同时可以根据DTD的内容给出自动提示的信息(Alt+/)

  • 如果没有DTD文件,那么就根据第二个双引号中的内容(URL地址),去网络中下载并读取、验证

1.8、XML解析,使用JAXP、DOM4j

DOM4j和DOM都具有节点树结构,DOM4j集中了DOM和SAX的优点

1.8.1、DOM解析

  • DOM(Document Object Model)文档对象模型,是 W3C 组织推荐的处理 XML 的一种方式。

  • 使用DOM方式解析,要求解析器把整个XML文档装载到一个Document对象中。Document对象包含文档元素,即根元素,根元素包含N个子元素

  • DOM适用于XML文档不大的情况,因为需要先将整个XML加载到内存

  • 特点

  1. 加载XML全部内容

  2. 生成Document对象

  3. 所有标签元素对象都在Document对象中获取

  • DOM使用步骤:

  1. 创建DOM工厂类对象DocumentBuilderFactory.newInstance()

  2. 通过DOM工厂类对象创建DOM解析器对象.newDocumentBuilder(),得到DocumentBuilder对象

  3. 通过DOM解析器对象创建xml文件解析对象.parse(File对象或路径),进行解析xml文件,得到Document对象

  4. 通过xml文件解析对象获取所有的student节点对象,.getElementsByTagName("student"),得到NodeList对象

  5. 遍历student节点对象,并将节点对象中的子节点的文本给Student对象进行赋值,最后存入List集合

  • 常用方法:nodelist.item(i)、element.getAttribute("id")、element.getChildNodes()、node.getNodeType()、Node.TEXT_NODE、Node.ELEMENT_NODE、node.getNodeName()、node.getFirstChild().getNodeValue()

try {
  // 1、创建DOM工厂类对象
  DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
    
  // 2、创建DOM解析器对象
  DocumentBuilder dom = domFactory.newDocumentBuilder();
    
  // 3、调用解析方法,解析xml文件
//  File file = new File("src/jaxp/students.xml");//也可以直接传一个file
  Document xmlDocument = dom.parse("src/jaxp/students.xml");
    
  // 4、获取所有student节点对象
  NodeList studentNodes = xmlDocument.getElementsByTagName("student");
  List<Student> students = new ArrayList<Student>();
    
  // 5、去遍历每一个student节点,取出每个student节点中的子节点及内容
  for (int i = 0; i < studentNodes.getLength(); i++) {
    Student student = new Student();
    // 通过下标获取Element
    Element node = (Element) studentNodes.item(i);
    
    // 获取该节点属性内容
    int id = Integer.parseInt(node.getAttribute("id"));
    student.setId(id);
    student.setMoney(Integer.parseInt(node.getAttribute("money")));
    
    // 通过节点对象获取所有子节点对象  
    NodeList childNodes = node.getChildNodes();
    for (int j = 0; j < childNodes.getLength(); j++) {
      // 获取当前节点类型,去除文本内容
      Node item = childNodes.item(j);
      // 判断节点类型是否为元素节点,还有Node.TEXT_NODE,文本节点内容
      if (item.getNodeType() == Node.ELEMENT_NODE) {
        // 获取该节点名字
        String nodeName = item.getNodeName();
        // 获取文本内容
        String nodeValue = item.getFirstChild().getNodeValue();
        switch (nodeName) {
        case "name":
          student.setName(nodeValue);
          break;
        case "age":
          student.setAge(Integer.parseInt(nodeValue));
          break;
        case "gender":
          student.setGender(nodeValue);
          break;
        case "hobby":
          student.setHobby(nodeValue);
          break;
        default:
          break;
        }
      }
    }
    students.add(student);  //将赋值完的student加入集合
  }
  students.forEach(System.out::println);
}

1.8.2、SAX解析

  • SAX,(Simple API for XML)它不是W3C标准,但它是 XML 社区事实上的标准,因为使用率也比较高

  • 使用SAX方式解析,每当读取一个开始标签、结束标签或者文本内容的时候,都会调用我们重写的一个指定方法,该方法中编写当前需要完成的解析操作

  • 直到XML文档读取结束,在整个过程中,SAX解析方法不会在内存中保存节点的信息和关系。

  • 使用SAX解析方式,不会占用大量内存来保存XML文档数据和关系,效率高。

  • 但是在解析过程中,不会保存节点信息和关系,并且只能从前往后,顺序读取、解析

  • SAX适用于数据量大的时候,因为读到哪解析到哪

  • 特点

  1. 一次读取一个标签,读取到一个标签就开始解析,不会保存节点信息

  2. 读取到开始标签、文本内容、结束标签都会自动调用对应方法

  3. 5个常用SAX事件

  4. startDocument(),解析器发现了文档的开始标签 ,会自动调用该方法

  5. startElement(),解析器发 现 了一个起始标签,会自动调用该方法

  6. character(),解析器发 现 了标签 里 面的文本值,会自动调用该方法

  7. endElement(),解析器发 现 了一个结束标签,会自动调用该方法

  8. endDocument(),解析器发 现 了文档结束标签,会自动调用该方法

  • SAX使用步骤:

  1. 创建SAX工厂类对象SAXParserFactory.newInstance()

  2. 通过SAX工厂类对象创建SAX解析器对象.newSAXParser(),得到SAXParser对象

  3. 创建一个StudentHandler类去继承DefaultHandler,重写父类的五个方法,然后创建出该类的实例对象,最好在StudentHandler创建能遍历输出List集合的方法,也可以把这块写在endDocument中

  4. 通过SAX解析器对象使用.parse(File对象或路径, DefaultHandler对象)进行解析xml文件,这个过程中会自动调用我们的StudentHandler中重写的方法

public class StudentSAX {
    public static void main(String[] args) {
        try {
            // 1、创建SAX工厂类对象
            SAXParserFactory saxFactory = SAXParserFactory.newInstance();
            // 2、获取SAX解析器对象
            SAXParser sax = saxFactory.newSAXParser();
            // 3、调用解析方法,解析xml文件
            StudentHandler handler = new StudentHandler();
            sax.parse("src/jaxp/students.xml", handler);
            handler.getList().forEach(System.out::println);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
class StudentHandler extends DefaultHandler {
    private List<Student> students = null;
    private Student student = null;
    private String tagName = null;// 当前标签名
    // 开始文档解析
    @Override
    public void startDocument() throws SAXException {
        students = new ArrayList<Student>();
    }
    // 读取开始元素(开始标签)
    // uri:文件路径,localName:命名空间,qName:开始标签名,attributes:开始标签的属性
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if ("student".equals(qName)) {
            student = new Student();
            student.setId(Integer.parseInt(attributes.getValue("id")));
            student.setMoney(Integer.parseInt(attributes.getValue("money")));
        } else {
            tagName = qName;
        }
    }
    // 读取文本内容
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String content = new String(ch, start, length);
        content.trim();// 去除前后空格
        if ("name".equals(tagName)) {
            student.setName(content);
        } else if ("age".equals(tagName)) {
            student.setAge(Integer.parseInt(content));
        } else if ("gender".equals(tagName)) {
            student.setGender(content);
        } else if ("hobby".equals(tagName)) {
            student.setHobby(content);
        }
    }
    // 读取结束元素(结束标签)
    // uri:文件路径,localName:命名空间,qName:结束标签名
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        tagName = null;
        if ("student".equals(qName)) {
            students.add(student);
        }
    }
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
    public List<Student> getList() {
        return students;
    }
}

1.8.3、DOM4j

  • DOM4j需要使用jar包,它是一个十分优秀的Java XML API,具有性能优异、功能强大和极其易使用的特点,且开源免费

  • 集合了DOM、SAX的优点,使用了节点树结构

1.8.3.1、DOM4j读取xml文件

  • 使用DOM4j读取xml文件的使用步骤

  1. 创建SAXReader对象new SAXReader()

  2. 使用SAXReader对象解析xml文件.read(File对象或路径),得到Document对象

  3. 通过Document对象获取根节点.getRootElement(),得到Element对象

  4. 通过根节点获取所有子节点集合.elements(),得到List<Element>对象

  5. 遍历子节点集合,获取属性集合.attributes()、属性的值.getValue()、子节点集合.elements()、子节点的文本内容.getText(),把这些给Teacher对象赋值后加入到List集合中

try {
  // 1、创建解析器对象
  SAXReader saxReader = new SAXReader();
  
  // 2、解析xml文件
  Document document = saxReader.read("src/jaxp/teachers.xml");
  
  // 3、获取根节点
  Element rootElement = document.getRootElement();
//  System.out.println(rootElement.getName());
  
  // 4、通过根节点获取其所有子节点集合
  List<Element> elements = rootElement.elements();
  List<Teacher> teachers = new ArrayList<Teacher>();
  
  // 5、遍历子节点集合
  for (Element element : elements) {
    Teacher teacher = new Teacher();
    // 获取teacher的属性集合
    List<Attribute> attributes = element.attributes();
    // 循环teacher的属性
    for (Attribute attribute : attributes) {
      teacher.setId(Integer.parseInt(attribute.getValue()));
    }
  
    // 获取teacher的子节点
    for (Element e : element.elements()) {
      switch (e.getName()) {
      case "name":
        teacher.setName(e.getText());
        break;
      case "age":
        teacher.setAge(Integer.parseInt(e.getTextTrim()));
        break;
      case "salary":
        teacher.setSalary(Double.parseDouble(e.getTextTrim()));
        break;
      default:
        break;
      }
    }
    teachers.add(teacher);
  }
  teachers.forEach(System.out::println);
}

1.8.3.2、DOM4j创建并写xml文件

  • 使用DOM4j创建并写xml文件的使用步骤

  1. 得到你想要写入的数据,例如List集合

  2. 创建Document对象DocumentHelper.createDocument()

  3. 通过Document对象给Document对象添加节点document.addElement("teas"),因为是第一个也就是根节点了,得到Element对象

  4. 循环你要添加的子节点个数,也可以遍历List,然会通过根节点来给根节点添加子节点.addElement("tea"),得到Element对象

  5. 再通过子节点来给子节点添加属性.addAttribute("id", String.valueOf(teacher.getId()))

  6. 再通过子节点给子节点添加子节点,得到Element对象

  7. 然后再通过子节点的子节点来添加文本内容.addText(String.valueOf(teacher.getId()))

  8. 通过以上步骤的Document就被添加完成了,可以通过XMLWriter对象来写入(write),创建XMLWriter对象,要给输出流、Format作参数

XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("src/jaxp/teas.xml"),
                    OutputFormat.createPrettyPrint());
List<Teacher> teachers = new ArrayList<Teacher>();
teachers.add(new Teacher(1, "潘广闯", 18, 100000));
teachers.add(new Teacher(2, "孙江宝", 18, 80000));
teachers.add(new Teacher(3, "崔略", 18, 120000));
​
// 1、创建document对象
Document document = DocumentHelper.createDocument();
Element teas = document.addElement("teas");
for (Teacher teacher : teachers) {
  //给子节点添加属性
  Element tea = teas.addElement("tea");
  tea.addAttribute("id", String.valueOf(teacher.getId()));
  
  //给子节点添加id子节点
  Element id = tea.addElement("id");
  id.addText(String.valueOf(teacher.getId())); // 或者teacher.getId()+""
  
  Element name = tea.addElement("name");
  name.addText(teacher.getName());
  
  Element age = tea.addElement("age");
  age.addText(String.valueOf(teacher.getAge()));
  
  Element salary = tea.addElement("salary");
  salary.addText(String.valueOf(teacher.getSalary()));
}
​
try {
  XMLWriter xmlWriter = new XMLWriter(
    new FileOutputStream("src/jaxp/teas.xml"),
    OutputFormat.createPrettyPrint());
  xmlWriter.write(document);  //将Document对象写入
  xmlWriter.flush();
  xmlWriter.close();
}

3、JDBC

3.1、Junit

JUnit,是一个Java语言的单元测试框架,用于编写和运行可重复的测试

3.1.1、Junit5的三个模块

JUnit5 与以前版本的 JUnit 不同,拆分成由三个不同子项目的几个不同模块组成:

  • JUnit Platform: 用于JVM上启动测试框架的基础服务,提供命令行,IDE和构建工具等方式执行测试的支持

  • JUnit Jupiter:包含 JUnit 5 新的编程模型和扩展模型,主要就是用于编写测试代码和扩展代码

  • JUnit Vintage:用于在JUnit 5 中兼容运行 JUnit3.x 和 JUnit4.x 的测试用例

3.1.2、导入Junit5

3.1.2.1、eclipse

右键项目名 -> build path -> add libraries -> Junit5 -> finish

3.1.2.2、maven

<!-- 核心功能的依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
​
<!-- 参数化测试功能的依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>

3.1.2、使用Junit5

@DisplayName 用于标注测试类和测试方法的名字
@Test 用于标注这个方法是测试方法
@BeforeAll 在所有测试方法开始之前,执行一次(静态方法)
@AfterAll 在所有测试方法结束之后,执行一次(静态方法)
@BeforeEach 在每一个测试方法,执行开始之前,都会执行(非静态方法)
@AfterEach 在每一个测试方法,执行结束之后,都会执行(非静态方法)
@Disabled 注解,那么在运行这个测试类的时候,该方法会被忽略
​
去掉 @Test ,而使用 @RepeatedTest 注解,则表示该方法要重复测试
@RepeatedTest 注解内,可以使用以下几个变量:
currentRepetition ,表示已经重复的次数
totalRepetitions ,表示总共要重复的次数
displayName ,表示测试方法显示名称
​
使用@ValueSource 配合 @ParameterizedTest 注解,可以表示当前测试方法,需要进行传参测试
@ValueSource 支持 Java 的基本类型、字符串,Class,使用时参数以数组方式传递
@ParameterizedTest 替代 @Test 注解,任何一个参数化测试方法都需要标记上该注解
@DisplayName("单元测试类")
public class JunitTest {
    @BeforeAll
    public static void init() {
        System.out.println("初始化数据");
    }
    @AfterAll
    public static void cleanUp() {
        System.out.println("清理数据");
    }
    @BeforeEach
    public void methoedStart() {
        System.out.println("当前测试方法开始");
    }
    @AfterEach
    public void methoedEnd() {
        System.out.println("当前测试方法结束");
    }
    @DisplayName("测试1")
    @RepeatedTest(value = 3, name = "{displayName} 的第 {currentRepetition} 次测试")
    void test1() {
        System.out.println(" 测试1执行");
    }
    @DisplayName("测试2")
    @Test
    void test2() {
        System.out.println(" 测试2执行");
    }
    @ParameterizedTest
    @ValueSource(strings = { "tom", "mary", "poly" })
    void test3(String name) {
        System.out.println(" hello!" + name);
    }
}

3.2、JDBC

  • JDBC(Java DataBase Connectivity)Java数据库连接(技术),也就是能够通过java代码,连接到数据库,并可以使用SQL语句,对数据库进行各种操作

  • 即把Java代码中的SQL语句送进数据库

  • JDBC ,是一种技术规范,它制定了程序中连接不同数据库的标准API

  • JDBC就相当于Java版本的ODBC

  • JDBC就是Java与数据库的接口(标准),即API

  • 程序员使用Sun公司实现的JDBC和jar包,让JDBC连接、操作数据库

3.2.1、历史版本ODBC

最早连接数据库的标准还有 ODBC(Open Database Connectivity),开放式数据库连接。使用C语言实现。

它提供了一套连接并操作数据库的API接口,使得程序员可以使用同一套标准的代码,去访问不同数据库,而不需要改变代码。

ODBC提供了一套用来访问数据库的标准API,同时提供了驱动管理器,允许第三方开发驱动程序连接特定的数据库,并将驱动注册到驱动管理器里面

3.2.2、驱动

  • JDBC-ODBC桥 此类驱动程序将JDBC调用转换成ODBC调用 , 然后使用一个ODBC驱动程序与数据库进行通信

早期Java包含了驱动程序: sun.jdbc.odbc.JdbcOdbcDriver。但是使用之前需要对ODBC数据源进行配置。JDK1.8不再提供JDBC/ODBC桥的支持

  • 本地API驱动 用来和数据库的客户端API进行通信。

使用前,需要安装Java类库和一些平台相关的代码(特定的驱动程序,类似ODBC)

通过驱动程序的转换,把Java程序中使用的JDBC API转换成NativeAPI,进而存取数据库

  • 网络协议驱动 使用与具体数据库无关的协议,将数据库请求发送给服务器中间件。

中间件服务器,再将数据库请求翻译成符合数据库规范的调用,再把这种调用传给数据库服务器。

由于在交互中间多了一层中间件,所以执行效率较低。

  • 本地协议驱动 将JDBC请求直接转换成符合相关数据库系统规范的请求。

可以直接和数据库通信,这种类型的驱动完全由Java实现,因此实现了平台独立性。

执行效率相对于其他三种类型,不需要将jdbc调用转换成odbc/本地数据库接口/中间层服务器,效率高。我们常用的驱动方式,也是第四类驱动

3.2.3、连接

在使用JDBC操作数据库之前,需要先获取到数据库的连接对象,而获取连接对象之前,需要先注册驱动到驱动管理器中,而注册驱动之前,需要先加载驱动类到内存中

  1. 加载驱动类

  2. 将驱动类注册到驱动管理器中(可以自动完成),加载后自动完成注册

  3. 通过驱动管理器获取数据库连接对象

3.2.3.1、加载驱动类

java.sql.Driver:是JDBC中提供的驱动接口,每一种数据库的驱动类都要实现这个接口。

java.sql.DriverManager:是JDBC中提供的驱动管理器(类),注册驱动后,可通过它获取到数据库连接对象

3.2.3.1.1、加载驱动类的三种方式

每开个Driver都会开个JVM

public class JDBCDriverTest {
    @DisplayName("加载驱动类方法1")
    @Test
    public void test1() {
        String driverClass = "oracle.jdbc.driver.OracleDriver";
        try {
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
​
    @DisplayName("加载驱动类方法2")
    @Test
    public void test2() {
        // 创建出驱动对象
        Driver driver = new oracle.jdbc.driver.OracleDriver();
    }
​
    @DisplayName("加载驱动类方法3")
    @Test
    public void test3() {
        // 设置环境变量
        // jdbc.drivers=oracle.jdbc.driver.OracleDriver
        // java -Djdbc.drivers=oracle.jdbc.driver.OracleDriver com.chw.test.Hello
        // 驱动管理器会自动加载该驱动类
        System.setProperty("jdbc.drivers", "oracle.jdbc.driver.OracleDriver");
        System.out.println(System.getProperty("jdbc.drivers"));
    }
}
  • 其中,第三种方式,还可以在运行java程序的时候,通过给JVM传参的方式进行设置: Eclipse中选择Window=>Preferences=>Java=>Installed JRES=>选中安装的jdk或者jre并进行编辑=>在Default VM Arguments中输入需要设置的jvm参数=>点击Finish完成设置。

-Djdbc.drivers=oracle.jdbc.driver.OracleDriver

  • 还可以在代码中右键=>run configurations=>VM arguments进行设置

3.2.3.1.2、注册驱动的三种方式
  1. Class.forName("驱动全限定名");

  2. DriverManager.registerDriver(驱动实例)

  3. 使用jdbc.drivers 选项来指定,在运行程序时,使用-D选项,如: java -Djdbc.drivers=oracle.jdbc.driver.OracleDriver

3.2.3.2、获取连接

  • 获取数据库连接对象,需要三个参数:

  1. url,需要连接的数据库地址

  2. user,登录数据库的用户名

  3. password,登录数据库的密码 这是Oracle本地协议

String url = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL"; 

thin 是oracle的一种连接方式 后边必须有数据库所在主机的IP和PORT

XE表示连接的oracle数据库的名字

3.2.3.2.1、获取数据库连接对象的种方式
//第一种
Class.forName(driverClass); // 静态代码块中做了装载,以及自动加载
Connection conn = DriverManager.getConnection(url, user, password);
​
//第二种
OracleDriver driver = new OracleDriver();
//通过上面的OracleDriver类的反编译源码可知,我们不手动注册的话,它也会自动注册的
DriverManager.deregisterDriver(driver);
conn = DriverManager.getConnection(url,user,password);
​
//第三种
System.setProperty("jdbc.drivers","oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(url,user,password);
​
//第四种,在没有主动加载或配置驱动类的情况下,也可以获取oracle数据库的连接对象,
//那是因为在当前使用的驱动包中,已经默认配置了需要加载的驱动类
//就在oracle的jar包的这个目录下:/META-INF/services/java.sql.Driver
Connection conn = DriverManager.getConnection(url,user,password);

3.2.3.3、获取properties配置文件内容

Properties pro = new Properties();
pro.load(new FileInputStream("src/oracle.properties"));
driverClass = pro.getProperty("driverClass");
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");

3.2.3.4、使用DOM4j从xml文件中获取Connection

public class DOM4jConnectionTest {
    private static String driverClass;
    private static String url;
    private static String user;
    private static String password;
    Connection conn = null;
    @Test
    public void connectionByDOM4j() {
        try {
            new SAXReader()
            .read("src/com/chw/jdbc/oracle.xml")
            .getRootElement()
            .elements()
            .forEach((e) -> {
                switch (e.getName()) {
                case "driverClass":
                    driverClass = e.getText();
                    break;
                case "url":
                    url = e.getText();
                    break;
                case "user":
                    user = e.getText();
                    break;
                case "password":
                    password = e.getText();
                    break;
                default:
                    break;
                }
            });
            Class.forName(driverClass);
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.2.3.5、使用src目录寻找文件的缺点

  • 如果将来项目成型打包,最终变成jar/war,就没有src目录了,文件就找不到了

  • 最好通过此方法: Properties properties = new Properties();

InputStream inputStream =

当前类名.class.getClassLoader().getResourceAsStream("文件名");

properties .load(inputStream);

3.2.4、JDBC的六大步骤

  1. 注册驱动(自动)Class.forName(driverClass); 一般程序员加载驱动类Class.forName(driverClass)后就自动注册

  2. 获取数据库连接Connection conn = DriverManager.getConnection(url, user, password);

  3. 创建 Statement类型或子类型

  4. 执行sql语句

  5. 处理结果集(一般查询语句才需要处理)

  6. 关闭资源

3.2.5、Statement

  1. 用于执行静态 SQL 语句并返回它所生成结果的对象

  2. 在默认情况下,同一时间每个 Statement 对象只能打开一个 ResultSet 对象。

  3. 因此,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的 Statement 对象生成的

  • 如果有多条sql语句要执行,他们的格式相同,只是要操作的数据不同,那么可以称他们为同构的sql语句,Statement处理大量的同构的SQL语句没PreparedStatement快

  • 在Statement的SQL语句中想放入自定义的字符串对象,要 '"+nowStr+"'

  • Java的Statement都有默认的自动提交事务conn.setAutoCommit(false);可以取消自动提交事务,

  • 自动提交事务是在conn关闭的时候自动提交的

3.2.5.1、常用方法

java.sql.ResultSet接口,专门用来表示select查询语句返回的结果集,表示当前sql的查询结果

  • boolean execute(String sql) throws SQLException 执行sql,返回boolean类型数据

true表示返回结果是一个ResultSet

false表示返回结果是一个数据更新的条数或者没有返回结果

  • int executeUpdate(String sql) throws SQLException 执行sql,返回int类型数据

返回0,表示没有结果返回

返回其他数字,表示操作数据的条数

  • ResultSet executeQuery(String sql) throws SQLException 执行sql,返回ResultSet 类型结果

一般查询语句使用该方法,可以返回结果集ResultSet ,然后再遍历结果集,拿到每条数据

  • int[] executeBatch() throws SQLException 执行批处理操作,返回每条sql命令的执行结果,放到int数组中

数组中每个数字对应一条命令影响的行数,但是在Oracle的驱动中没有实现该功能,即插入数据SQL命令提交成功后不会返回影响的行数,则返回-2

3.2.5.2、PreparedStatement(Statement的子接口)

  • 表示预编译的 SQL 语句的对象

  • PreparedStatement执行大量同构SQL语句时才明显快 简称PS,它除了拥有Statement 的功能特点之外,它还有有着自己的特

点:可以对sql语句进行预处理

Statement,是每次执行一个sql语句,就要把一个完整的sql语句发送给数据库进行执行,然后取回返回的结果。

PreparedStatement,可以把一个sql语句的结构,提前发送给数据库进行预处理,然后在专门给发送要操作的具体的值,在数据量大的时候,这种方式会大大提高执行效率

使用批处理,在set完后直接addBatch方法就可以,最后executeUpdate

3.2.5.2.1、PreparedStatement的优点
  • 使用 Statement,每次都需要,使用字符串拼接出一个完整的sql语句,再发送给数据库,如果sql语句比较复杂,需要拼接的数据比较多,那么在字符串拼接过程中,就很容易出错误。

  • Statement传输Date类型的数据很麻烦,需要to_date函数

  • 使用 PreparedStatement,就不需要拼接字符串,因为只需要先把带占位符的sql发给数据库,之后在专门发送数据,代替占位符就可以了,这里基本不会出现在sql中拼接字符串的情况

3.2.5.3、CallableStatement(PreparedStatement的子接口)

  • 用于执行 SQL 存储过程的接口 简称CS,它除了拥有 俩个父接口 的功能特点之外,它还有有着自己的特

点:可以调用数据库中的存储过程

3.2.5.3.1、在数据库中创建存储过程
  • 先有t_user表

create table t_user(
id number primary key,
name varchar2(100),
salary number
);
  • 在数据库中创建存储过程

create or replace procedure insert_user_procedure (id in number,
name in varchar2,salary in number,result out varchar2) is
begin
insert into t_user values(id,name,salary);
result:='调用存储过程成功';
Exception
when others then
result:= '调用存储过程出错: ' || SQLERRM;
end;
/
  1. 参数列表中的in代表输入参数,out代表返回值

  2. SQLERRM表示执行sql时候出现的错误信息(sql-error-message)

  3. sql中的字符串拼接使用双竖杠(||)

  • JDBC中使用CS(CallableStatement)调用存储过程

public class CSTest {
  private String driverClass = "oracle.jdbc.driver.OracleDriver";
  private String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
  private String user = "chw";
  private String password = "chw";
​
  @Test
  public void cs() {
    Connection conn = null;
    CallableStatement cs = null;
    
    try {
      //1.加载注册驱动
      Class.forName(driverClass);
      
      //2.获取连接对象
      conn = DriverManager.getConnection(url,user,password);
      
      //3.获取cs对象
      cs = conn.prepareCall("{call insert_user_procedure(?,?,?,?)}");
      cs.setObject(1, 1);
      cs.setObject(2, "tom1");
      cs.setObject(3, 4500);
      cs.registerOutParameter(4, java.sql.Types.VARCHAR);
      
      //4.执行存储过程
      cs.execute();
      
      //5.处理结果
      //获取第四个参数的值,也就是返回结果
      Object result = cs.getObject(4);
      System.out.println("result = "+result);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      //把资源关掉
    }
  }
}

3.2.5.4、Statement与PreparedStatement的区别

  • Statement是每次执行一个sql语句,就要把一个完成的sql语句发送给数据库进行执行,然后取回返回的结果

  • PreparedStatement是预编译的sql语句对象,可以把一个sql语句的结构,提前发送给数据库进行预处理,然后在专门给发送要操作的具体的值。sql语句结构中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。

  • 使用PrepareStatement对象执行sql时,sql被数据库进行解析和编译,然后被放到命令缓冲区,每当执行同一个PreparedStatement对象时,它就会被解析一次,但不会被再次编译。

  • 在缓冲区可以发现预编译的命令,并且可以重用。

  • PrepareStatement可以减少编译次数,提高数据库性能

  • 但是PrepareStatement的预编译空间有限

  • PrepareStatement可以防止SQL注入,如: or 1=1;

3.2.6、transaction(事务)

  • 主要作用就是当事务量很大的时候,你改成手动提交后,就不会执行一条提交一次了,而是全部做完之后统一提交,这样可以提高效率

  • 默认情况下,在JDBC中执行的DML语句,所产生的事务,都是自动提交的,也就是,每执行一次DML语句,所产生的事务,就会自动提交,conn关闭的时候也会自动提交

  • 手动提交需要在catch的Exception中加入conn.rollback方法,只要出现异常就回滚,最好是只有一个最大的Exception异常

  • conn.setAutoCommit(false);关闭自动提交功能

  • conn.commit();手动提交事务

  • 改成手动提交时,finally语句中的 conn.close() 代码,也可以提交事务

  • Savepoint p1 = conn.setSavepoint("p1"); 设置回滚点

3.2.7、连接池(池化技术)

  • 池(Pool)技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低系统资源开销

  • 数据库连接池是有大小的,即对象有上限

  • 例如,数据库连接池,在系统初始化时创建一定数量数据库连接对象Connection,需要时直接从池中取出一个空闲对象,用完后并不直接释放掉对象,而是再放到对象池中,以便下一次对象请求可以直接复用。

  • 这样可以消除对象创建和销毁所带来的延迟,从而提高系统的性能

  • 在当前数据库环境中,创建出一定数据的数据库连接对象之后,就无法在创建新对象了

  • 而数据库连接池,就可以在种情况下,预先创建出一批数据库连接对象,然后对它们进行管理,并反复使用,这样就不需要频繁的销毁和创建了,提供了资源的利用率 Druid连接池是阿里巴巴开源的数据库连接池项目

  • 连接池拿出来的对象的getClass和JDBC的getClass是不同的,因为JDBC获取的连接是Driver的实际连接,而连接池的连接是从连接池获取的空闲连接对象,

  • 所以JDBC的关闭资源是真正关闭资源,而连接池的关闭资源是让连接对象返回连接池,变回空闲连接对象 JAVA EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其标记为忙。

如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量由配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接

3.2.7.1、使用连接池

修改单个文件的编码格式,右键文件,选Properties,改编码格式

javax.sql.DataSource是Java中定义的一个数据源标准的接口,通过它获取到的数据库连接对象,

  • 如果调用 close() 方法,不会再关闭连接,而是归还连接到连接池中

Properties properties = new Properties();
InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(inputStream);
Connection conn = DruidDataSourceFactory.createDataSource(properties).getConnection();

3.2.7.2、手动配置连接池

public class ConnectionTest {
  private String driverClass = "oracle.jdbc.driver.OracleDriver";
  private String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
  private String user = "chw";
  private String password = "chw
";
  // 手动设置属性,创建连接池对象
  @Test
  public void test_druid() {
    // 创建数据库连接池
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driverClass);
    dataSource.setUrl(url);
    dataSource.setUsername(user);
    dataSource.setPassword(password);
​
    // 设置创建连接池时创建多少个连接
    dataSource.setInitialSize(5);
​
    // 设置最大连接数,当从连接池里面获取的连接超过了5个会继续创建,超过了10个就需要等待
    dataSource.setMaxActive(10);
    Connection conn = null;
​
    // 获取连接
    try {
      conn = dataSource.getConnection();
    } catch (SQLException e) {
      e.printStackTrace();
    }finally {
      if(conn!=null) {
        try {
          conn.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if(dataSource!=null) {
         dataSource.close();
      }
    }
  }
}

3.2.8、封装

  • 封装就是我们帮用户把需要的功能用方法封装起来,让用户只需要使用方法即可完成业务

  • 所有的变量或方法都是static的

3.2.8.1、封装连接池

  • 在static代码块中封装properties.load(通过当前类的类加载器获取指定文件名的资源)

  • 再将全局静态变量DataSource通过装载好的properties创建出来DruidDataSourceFactory.createDataSource(properties)

static {
    try {
        Properties properties = new Properties();
        properties.load(JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
        dataSource = DruidDataSourceFactory.createDataSource(properties);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

3.2.8.2、封装getConnection和close

  • 获取数据库连接就是通过连接池获取一个空闲连接对象

  • 关闭资源就通过rs,st,conn这个最大的方法进行重载

public static Connection getConnection() throws SQLException {
    return dataSource.getConnection();
}
​
public static void close(ResultSet rs, Statement st, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

3.2.8.3、封装DML语句

  • 就接收一个sql字符串,然后正常执行,返回int

  • 这里封装的是Statement的方法,如果要封装PreparedStatement的方法,还需要传个集合或可变参数进来

public static int executeDML(String sql) {
        int rows = 0;
        Connection conn = null;
        Statement st = null;
        try {
            conn = getConnection();
            st = conn.createStatement();
            rows = st.executeUpdate(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(st, conn);
        }
        return rows;
    }

3.2.8.3、封装DDL语句

  • 就接收一个sql字符串,然后正常执行

  • 因为DDL与DML执行方法一致,所以可以交给DML去做

public static void executeDDL(String sql) {
    executeDML(sql);
}

3.2.8.4、封装DQL语句

  • 因为是DQL语句,所以有返回值,且返回值要么是一个实体类对象,要么是多个实体类对象

  • 但是为了通配各种实体对象,我们不可以指定方法的返回类型,所以就需要使用泛型

3.2.8.4.1、使用Function函数式接口将多个返回结果封装成List集合
  • 我们可以将得到的ResultSet封装成List集合,但是我们并不知道我们要取的列名叫什么,也不知道该列名的数据类型

  • 所以我们要使用Function这个函数式接口,传进去ResultSet对象,返回T(泛型)对象

  • 我们负责从数据库拿到数据集,我们也帮忙循环rs.next,但是具体要取什么类型的什么名字的数据需要用户来做

public static <T> List<T> executeDQLforList(
  String sql, Function<ResultSet, T> f) {
        List<T> list = new ArrayList<T>();
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            st = conn.createStatement();
            rs = st.executeQuery(sql);
            while (rs.next()) {
                list.add(f.apply(rs));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(st, conn);
        }
        return list;
    }
​
//  使用
@Test
void test_DQLforList1() {
    String sql = "select id,name,age from t_user";
    List<User> users = JDBCUtil.executeDQLforList(sql, rs -> {
        User obj = null;
        try {
            obj = new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return obj;
    });
    users.forEach(System.out::println);
}
3.2.8.4.2、使用Function函数式接口将返回结果封装,并作情况判断
  • 在返回List集合的基础上进行判断List的size是否大于1个,因为我们要的是单个返回结果,若大于1个就抛出异常,

  • 因为若我们不去判断,size为0的时候我们取拿就会报下标越界异常

public static <T> List<T> executeDQLforObject(
  List<T> list = executeDQLforList(sql, f);
        if (list.size() > 1) {
            throw new Exception("查询结果数量过多,有" + list.size() + "条,与期望不符!");
        }
        return list.size() > 0 ? list.get(0) : null;
    }
​
//  使用
@Test
void test_DQLforObject() {
    String sql = "select id,name,age from t_user where id=1";
    User user = null;
    try {
        user = JDBCUtil.executeDQLforObject(sql, rs -> {
            User obj = null;
            try {
                obj = new User(rs.getLong("id"), rs.getString("name"), rs.getInt("age"));
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return obj;
        });
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
    System.out.println(user);
}
3.2.8.4.3、使用反射将返回结果封装成List集合
  • 这个不需要用户去取什么类型的数据什么的,用户也不需要实现Function这个函数式接口的方法,用户只需要传sql和想要封装成的class类型

  • 我们通过反射,把用户给的class类型进行解析,实体类中需要什么类型的数据我们就用rs去取

  • 我们要解决的问题是:需要通过用户给的class类型知道字段名、字段类型,需要将获取到的值封装到泛型对象中

public static <T> List<T> executeDQLforList(String sql, Class<T> c) {
  T obj = null;  //将来要将数据库的值封装到该泛型对象中
  List<T> list = new ArrayList<T>();  //将每个封装好的obj加进来,最后返回
  
  // Map用来接收用户给的Class类型c的属性名和属性类型
  // key是类中的属性名,value是类中的属性类型
  // parseClass是我们自己写的方法,使用反射机制解析Class的所有属性
  Map<String, String> map = parseClass(c);
//  sql=generateSQL(map);  //我们可以再定义一个根据map创建SQL语句的方法,就不需要用户提供sql语句了
  
  Connection conn = null;
  Statement st = null;
  ResultSet rs = null;
  
  try {
    conn = getConnection();
    st = conn.createStatement();
    rs = st.executeQuery(sql);
    
    while (rs.next()) {
      obj = c.newInstance();  //让泛型对象得到指定类型的实例对象
      
      // attributeName就是map中的key,属性名
      // attributeType就是map中的value,属性值
      for (String attributeName : map.keySet()) {
        String attributeType = map.get(attributeName);
        Object value = null;  //用一个Object来获取任何的属性值
        switch (attributeType) {
        case "int":
          value = rs.getInt(attributeName);
          break;
        case "long":
          value = rs.getLong(attributeName);
          break;
        case "String":
          value = rs.getString(attributeName);
          break;
        default:
          break;
        }
      
        // 将值封装到泛型对象中,obj是引用类型,所以传的是引用
        setValue(obj, attributeName, value);
      }
      list.add(obj); // 将对象封装到list中
    }
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    close(st, conn);
  }
  return list;
}
​
/**
* 使用反射的方式,将值设置到对象的属性中
*
* @param obj      目标对象
* @param attrName 属性名
* @param value    属性值
*/
private static void setValue(Object obj, String attrName, Object value)
    throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
  Class<?> c = obj.getClass();  //不指定类型,也可以不写<?>
  
  // 开始反射解析
  Field declaredField = c.getDeclaredField(attrName);
  declaredField.setAccessible(true);
  declaredField.set(obj, value);
}
​
// 使用反射,解析指定类型,获取该类型的属性名字和属性类型
private static Map<String, String> parseClass(Class<?> c) {
  Map<String, String> map = new HashMap<String, String>();
  Field[] declaredFields = c.getDeclaredFields();
  for (Field field : declaredFields) {
    String fieldName = field.getName();
    String fieldType = field.getType().getSimpleName();
    map.put(fieldName, fieldType);
  }
  return map;
}
3.2.8.4.4、使用反射将返回结果封装
public static <T> T executeDQLforObject(String sql, Class<T> c) throws Exception {
        List<T> list = executeDQLforList(sql, c);
        if (list.size() > 1) {
            throw new Exception("查询结果数量过多,有" + list.size() + "条,与期望不符!");
        }
        return list.size() > 0 ? list.get(0) : null;
}

3.2.9、MetaData(获取数据库的所有信息)

  • 元数据(MetaData),即定义数据的数据 使用jdbc,可以查询到数据库中,指定表中的数据,与此同时,还可以获取到数据库本身的信息、表本身的信息、结果集本身的信息,这些都需用到MetaData:java.sql.DatabaseMetaData、java.sql.ResultSetMetaData

3.2.9.1、数据库中的 schema 与 catalog

  • 按照SQL标准的解释,在SQL环境下Catalog和Schema都属于抽象概念,主要用来解决命名冲突问题。

  • 就比如JVM解决命名冲突是ClassLoader+包+类名,这也解释了不同的ClassLoader装载同一个类也会产生两个不同的对象 从概念上说,一个数据库系统包含多个Catalog,每个Catalog又包含多个Schema,而每个Schema又包含多个数据库对象(表、视图、序列等)

数据库CatalogSchema
Oracle不支持用户名
MySQL不支持数据库名

3.2.9.2、Oracle删除表后产生的BIN开头的垃圾信息

3.2.9.2.1、查询垃圾信息
select object_name
from user_objects
where object_type = upper('table');
3.2.9.2.2、清除垃圾信息
purge recyclebin;

3.2.9.3、使用MetaData

3.2.9.3.1、通过DatabaseMetaData获取数据库的信息
Connection conn = JdbcUtils.getConnection();
​
//DatabaseMetaData实例的获取
DatabaseMetaData metaData = conn.getMetaData();
​
//获得数据库的名字
System.out.println(metaData.getDatabaseProductName());
​
//获得数据库的版本
System.out.println(metaData.getDatabaseProductVersion());
​
//获得数据库的主版本
System.out.println(metaData.getDatabaseMajorVersion());
​
//获得数据库的小版本
System.out.println(metaData.getDatabaseMinorVersion());
3.2.9.3.2、通过DatabaseMetaData获取数据库中,指定用户下的,所有表的名字
Connection conn = JdbcUtils.getConnection();
​
//DatabaseMetaData实例的获取
DatabaseMetaData metaData = conn.getMetaData();
​
//获得数据库中表的信息,null代表不指定表名,就是全都要
// metaData.getTables(catalog, schema, tableName, types);
rs = metaData.getTables(null, "chw", null, new String[]{"TABLE"});
​
while(rs.next()){
  String tableName = rs.getString("TABLE_NAME");//表名
  String typeName = rs.getString("TABLE_TYPE");//表的类型名称,table、view等
  System.out.println(tableName+" : "+typeName);
}
3.2.9.3.3、通过DatabaseMetaData获取数据库中指定用户下,指定表的,列的信息
Connection conn = JdbcUtils.getConnection();
​
//DatabaseMetaData实例的获取
DatabaseMetaData metaData = conn.getMetaData();
​
//获得表中列的信息
// metaData.getColumns(catalog, schema, tableName, columnName)
// rs = metaData.getColumns(null, "chw", "T_USER", "ID");
rs = metaData.getColumns(null, "chw", "T_USER", null);
​
while(rs.next()){
  String colName = rs.getString("COLUMN_NAME");//列名
  String typeName = rs.getString("TYPE_NAME");//类型名称
  System.out.println(colName+" : "+typeName);
}
3.2.9.3.4、通过ResultSetMetaData获取查询结集中,列的数量,每列的类型和名字
Connection conn = JDBCUtil.getConnection();
Statement st = conn.createStatement();
String sql = "select id myid,name,age from t_user where id=1";
ResultSet rs = st.executeQuery(sql);
ResultSetMetaData metaData = rs.getMetaData(); 
​
// 返回RusultSet结果中的列的数量
int columnNum = metaData.getColumnCount();
System.out.println("查询结果中的列数为: " + columnNum);
​
for (int i = 1; i <= columnNum; i++) {
  // 返回列序号为 i 的数据类型
  // 可以获取到别名
  String columnType = metaData.getColumnTypeName(i);
​
  // 返回此列的列名(字段名)
  String columnName = metaData.getColumnName(i);
  System.out.println(columnType + " : " + columnName);
}

3.2.10、由指定用户的指定表生成实体类

  • 想通过Oracle数据库中指定用户的指定表生成对应实体类,那用户必须要传的参数有:schema,即用户名、tableName,即表名、packageName,即想要生成在本项目下的什么路径下

3.2.10.1、生成步骤

  1. 解析指定的表,得到表中的 字段名、字段对应的类型,这个信息一看就得存在Map集合中,所以这个方法需要返回一个Map集合,map1

  • 记得要把用户给我们的schema和tableName变成大写

  1. 我们得到了数据表的字段和字段类型,我们就需要通过map1来得到实体类对应的属性名、属性类型的集合,map2

  • 值得注意的是,数据库中的数据类型名字与Java中的不一样,所以我们需要一个映射表,这个表肯定是Map集合,typeMapping,并且为了让这个集合一开始就能使用,我们把它放在static代码块中,并且记得把数据库的字段类型名变成大写

  1. 现在我们已经得到了实体类的属性名和属性类型,我们现在需要的就是该实体类的类名,这个通过表名就可以得到,规则自定

  2. 还需要得到这个实体类该放置的路径,我们可以通过System.Properties得到这个项目的工作路径,这个路径是绝对路径,然后再通过用户给的packageName和你自己设置的类名,就可以得到绝对路径

  • Windows中的路径有两种方式:src\com\和src/com,第一种是默认

  • 但是在Java中\是转义符,所以我们想通过拼接得到绝对路径,就会使用到分隔符,你要么把所有的\变成一个/,要么就使用4个\\来把得到路径中的一个\变成2个\,因为你是使用Java的流来写入的,四个\只是因为Java中的\路径需要用\来表示,然后你赋值的时候就需要用4个\

  1. 我们已经得到了要写的属性名和属性值,得到了要写的路径,还差要写的内容

  • package部分,将用户给的packageName放进去

  • import部分(可以没有)

  • class声明部分

  • class正文——属性部分

  • class正文——getter、setter部分

  1. 将得到的内容写入到指定文件下

3.2.10.2、代码

public class GenerateClassUtil {
    // typeMapping中保存数据库中类型和Java中类型的映射关系
    private static Map<String, String> typeMapping;
    static {
        typeMapping = new HashMap<String, String>();
        typeMapping.put("NUMBER", "int");
        typeMapping.put("VARCHAR2", "String");
        typeMapping.put("VARCHAR", "String");
    }
    public static void generateClass(String schema, String tableName, String packageName) {
        // 1、解析指定表,得到表中的 字段、字段类型类型 的信息 map1
        Map<String, String> map1 = parseTable(schema, tableName);
        // 2、将map1转换为map2,map2中是要生成类中的 属性、属性类型 的信息
        Map<String, String> map2 = transformFieldInfo(map1);
        // 3、将表名转换为要生成的类名
        String clasaName = transformClassName(tableName);
        // 4、获取要生成的Java文件所在的路径
        String javaFilePath = getJavaFilePath(packageName, clasaName);
        // 5、生成Java文件中要写入的内容 (String)
        String javaFileContent = generateJavaFileContent(packageName, clasaName, map2);
        // 6、把5中生成的内容写入到4中获取的Java文件中
        writeJavaFile(javaFileContent, javaFilePath);
        System.out.println("表" + tableName + "对应的实体类,已经自动生成!");
    }
    private static void writeJavaFile(String javaFileContent, String javaFilePath) {
        FileWriter out = null;
        File file = new File(javaFilePath);
        // 如果Java文件所在目录不存在,就创建
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        try {
            out = new FileWriter(file);
            out.write(javaFileContent);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private static String generateJavaFileContent(String packageName, String clasaName, Map<String, String> map2) {
        StringBuilder sb = new StringBuilder();
        // package部分
        sb.append("package " + packageName + ";").append("\n");
        sb.append("\n");
        // import部分
        // class声明开始
        sb.append("public class " + clasaName + "{").append("\n");
        // class正文-属性部分
        for (String attributeName : map2.keySet()) {
            String attributeType = map2.get(attributeName);
            sb.append("\t").append("private " + attributeType + " " + attributeName + ";").append("\n");
        }
        sb.append("\n");
        // class正文-方法部分
        for (String attributeName : map2.keySet()) {
            String attributeType = map2.get(attributeName);
            // getter
            sb.append("\t").append("public " + attributeType + " get" + initCap(attributeName) + "(){").append("\n");
            sb.append("\t\t").append("return " + attributeName + ";").append("\n");
            sb.append("\t").append("}");
            // setter
            sb.append("\t").append(
                    "public void set" + initCap(attributeName) + "(" + attributeType + " " + attributeName + "){")
                    .append("\n");
            sb.append("\t\t").append("this." + attributeName + " = " + attributeName + ";").append("\n");
            sb.append("\t").append("}");
        }
        sb.append("\n");
        // class声明结束
        sb.append("}").append("\n");
        return sb.toString();
    }
    private static String getJavaFilePath(String packageName, String className) {
        // com.chw.pojo
        // com/chw/pojo
        // src/com/chw/pojo
        // D:\SpringToolSuiteWorkPlace\jd2107test01\src\com\chw\pojo\xxx.java
        String basePath = System.getProperty("user.dir"); // 项目路径
        String separator = System.getProperty("file.separator"); // 分隔符
        String packagePath = null;
        // 把点变成路径分隔符
        // "\\"是一个\的意思,因为\是转义符
        if ("\\".equals(separator)) {
            // 我们要两个\\,所以需要转义两个\,就是\\\\
            // 即变成 src\\chw这种样子,因为Java中的\是转义符
            // 直接一个点在λ表达式和其他地方有特殊含义,所以要用空括号引起来
            packagePath = packageName.replaceAll("[.]", "\\\\");
        } else {
            packagePath = packageName.replaceAll("[.]", "/");
        }
        // 拼成绝对路径
        String javaFilePath = basePath + separator + "src" + separator + packagePath + separator + className + ".java";
        return javaFilePath;
    }
    private static String transformClassName(String tableName) {
        // t_user ---> T_user
        return tableName.substring(0, 1).toUpperCase() + tableName.substring(1).toLowerCase();
    }
    private static Map<String, String> transformFieldInfo(Map<String, String> map1) {
        Map<String, String> map2 = new HashMap<String, String>();
        // map1 ID NUMBER
        // map2 id int
        for (String colName : map1.keySet()) {
            String colType = map1.get(colName);
            String javaFieldName = colName.toLowerCase();
            String javaTypeName = typeMapping.get(colType);
            map2.put(javaFieldName, javaTypeName);
        }
        return map2;
    }
    private static Map<String, String> parseTable(String schema, String tableName) {
        Map<String, String> map1 = new HashMap<String, String>();
        Connection conn = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtil.getConnection();
            DatabaseMetaData metaData = conn.getMetaData();
            rs = metaData.getColumns(null, schema.toUpperCase(), tableName.toUpperCase(), null);
            while (rs.next()) {
                String colName = rs.getString("COLUMN_NAME");
                String typeName = rs.getString("TYPE_NAME");
                map1.put(colName, typeName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(rs, conn);
        }
        return map1;
    }
    private static String initCap(String name) {
        return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
    }
}

4、SVN(不重要,没有git好用)

  • Subversion(SVN),是一个自由开源的版本控制软件

  • 是本地服务器存储,用的不多

  • 要下载可以去官方下载,开源的

4.1、安装

安装顺序:VisualSVN Server -> TortoiseSVN -> LanguagePack -> 在eclipse中安装

即:服务器、客户端、语言包、开发工具安装(去Marketplace或压缩包)

5、Maven

Maven是Apache下的一个开源项目,它是一个创新的项目管理工具,它用于对Java项目进行项目构建、依赖管理及项目信息管理。(开源、免费、Java语言实现)

pom.xml结构:

<dependencies><dependency>

<build><plugins><plugin>

5.1、基础设置

5.1.1、作用

5.1.1.1、项目构建

是指一个项目清理、编译、测试、报告、打包、安装、部署等过程,Maven中把这些过程进行了标准化管理,可以通过一个命令,自动按照顺序,依次执行这里面的每一个步骤

  1. 清理clean:将之前编译得到的旧文件class字节码文件删除

  2. 编译compile:将java源程序编译成class字节码文件

  3. 测试test:自动测试,自动调用junit程序

  4. 报告report:测试程序执行的结果

  5. 打包package:动态Web工程打War包,java工程打jar包6.

  6. 安装install:Maven特定的概念,将打包得到的文件复制到maven仓库中的指定位置

  7. 部署deploy:将工程生成的结果放到服务器中或者容器中,使其可以运行

5.1.1.2、依赖管理

  • 是指Maven将项目中所依赖的外部jar包,进行统一的管理

  • 它会对项目中依赖的jar包自动管理

5.1.2、下载、安装、配置

5.1.2.1、下载

去Maven官网下载

5.1.2.2、安装

找个没文字的文件夹解压就好,最好只有一个带版本号的文件夹

5.1.2.3、配置

配置环境变量,可以只配置一个path,也可以配一个home再配一个path

是你的bin目录绝对路径

5.1.2.4、检查

全部做完之后,mvn -v检查是否成功

5.1.3、仓库

5.1.3.1、本地仓库

  • 项目中所依赖的第三方jar,最终都会下载存放到自己电脑中的本地仓库中,本地仓库的位置可以自己指定,也可以使用默认路径: ${user.home}/.m2/repository

  • 项目是先去找本地是否有jar,没有的话才会去中央仓库下载

5.1.3.2、中央仓库

  • 这是Maven官方提供的远程仓库,仓库中存放了,日常项目中所会使用到的几乎所有jar包,当我们自己电脑中的本地仓库中,没有要依赖的jar的时候,Maven默认会从中央仓库中查找并下载需要的jar包

  • 但是速度特别慢,因为是在国外

  • jar包网站https://mvnrepository.com/

5.1.3.3、私有仓库

  • 私有仓库,也称为私服,一般是由公司自己设立的,只为本公司内部共享使用

  • 可以使用 Nexus软件工具,来搭建局域网中的私有的远程仓库,也就是私服,我们使用的阿里云镜像就用了nexus

5.1.3.4、仓库配置

  • maven会先找 ${user.home}/.m2/settings.xml ,如果找不到,会再找安装目录的\conf\settings.xml

  • 因为默认情况下,本地仓库的位置是:${user.home}/.m2/repository ,如果想修改这个地址,可以在maven的配置文件中,进行配置

  • 当然也可以把本地仓库分出来,方便管理,分出来的时候记得修改安装目录中的conf的setting.xml,

<localRepository>D:\apache-maven-3.6.3-bin\MavenRepo</localRepository>
  • 配置路径为新的仓库路径和阿里云镜像,然后复制一份setting.xml到新的仓库中

<mirror>
  <id>nexus-aliyun</id>
  <!-- <mirrorOf>*</mirrorOf> -->
  <mirrorOf>central</mirrorOf>
  <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>

5.1.4、坐标

在maven中,每一个项目都有三个坐标(GAV),用来唯一标识这个项目

  • groupId ,该元素定义当前Maven项目隶属的实际项目, 一般用的名字:公司或者组织域名倒序+项目名

  • artifactId ,该元素定义当前maven项目表示的具体子项目(模块) 一般用的名字:模块名

  • version ,该元素定义当前maven项目的具体版本 一般用的名字:版本

5.1.4.1、依赖引入

  • <dependency> 标签表示依赖的意思

  • <scope> 是可选的标签,用来指定这个依赖的作用范围,test表示只有在测试的时候才有效

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13</version>
  <scope>test</scope>
</dependency>

5.1.5、目录结构

5.1.6、pom.xml的结构

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.chw.invoke</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
​
    <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>
        <hello.version>0.0.1-SNAPSHOT</hello.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>com.chw.demo</groupId>
            <artifactId>hello</artifactId>
            <version>${hello.version}</version>
        </dependency>
        
    </dependencies>
​
    <build>
        <!-- 打包后项目的名字,可以使用变量表示 -->
        <!-- 项目坐标中的artifactId标签只,就是打包后jar名字 -->
        <finalName>${project.artifactId}</finalName>
        
        <!-- 描述项目中资源的位置 -->
        <resources>
            <!-- 自定义资源,可以有多个 -->
            <resource>
                <!-- 资源目录 -->
                <directory>src/main/resources</directory>
                <!-- 包括哪些文件参与打包 -->
                <includes>
                    <include>**/*</include>
                </includes>
                <!-- 排除哪些文件不参与打包 -->
                <excludes>
                    <exclude>config/*</exclude>
                </excludes>
            </resource>
        </resources>
        
        
        <!-- 设置构建时候的插件 -->
        <plugins>
            <!-- 资源编码插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- 代码编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- 源码打包插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <!-- 绑定source插件到Maven的生命周期 (compile) -->
                        <phase>compile</phase>
                        <!--在生命周期后执行绑定的source插件的goals(jar-no-fork) -->
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- 可执行jar插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <configuration>
                    <!-- 设置打包时候生成dependency-reduced-pom.xml文件为false -->
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <!-- 绑定shade插件到Maven的生命周期 (package) -->
                        <phase>package</phase>
                        <!--在生命周期后执行绑定的shade插件的goals -->
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <!--指定程序入口类 -->
                                    <mainClass>com.chw.invoke.Demo</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

5.1.7、target目录

maven在运行(构建)构成中,所产生的文件都会默认存放到target目录

可以使用mvn clean清理掉target文件夹

5.1.8、打包

  • mvn package

  • 打包后会将生成的jar存到target目录中,并且打包时默认会执行测试程序

  • 打包时,也可以指定参数,跳过测试: mvn package -Dmaven.test.skip=true

  • 也可以先清空项目,再打包: mvn clean package

  • 也可以先清空项目,再打包,同时跳过测试: mvn clean package -Dmaven.test.skip=true

  • 注意,测试代码(test目录)是不会被打入jar包的

5.1.8、install

mvn install

如果maven项目A,想使用maven项目B中的代码,那么就需要将B项目先安装到仓库中,然后项目A通过项目B的坐标,从仓库中找到项目B的jar,并引用到项目A的classpath中,这时候就可以使用了

5.1.9、三种下载jar包的方式

5.1.9.1、直接或间接从中央仓库下载

5.1.9.2、有源码后在项目目录下的cmd中mvn install

5.1.9.3、在有的jar包的目录下cmd执行命令

mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=6 -
Dpackaging=jar -Dfile=ojdbc6.jar -DgeneratePom=true
​
mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc7 -Dversion=7 -
Dpackaging=jar -Dfile=ojdbc7.jar -DgeneratePom=true
​
mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc8 -Dversion=8 -
Dpackaging=jar -Dfile=ojdbc8.jar -DgeneratePom=true

5.1.10、compile(编译)

mvn compile 编译主程序(main目录中)

mvn clean test-compile 编译测试程序(test目录中)

5.1.11、scope范围

  • compile ,默认值,适用于所有阶段(开发、测试、部署、运行),jar包会一直存在所有阶段

  • provided ,只在开发、测试阶段使用,目的是不让将来部署的容器和你本地仓库的jar包冲突,因为有些jar包在将来部署的服务器或者容器中是存在的,所以打包的是就不需要把这些jar添加进来了,如servlet相关jar包

  • runtime ,只在运行时使用,如JDBC驱动,适用运行和测试阶段。

  • system ,类似provided,需要显式提供包含依赖的jar的路径,Maven不会在Repository中查找它

  • test ,只在测试时使用,用于编译和运行测试代码。不会随项目发布,如JUnit相关jar包

5.2、在开发工具中使用

5.2.0、pom.xml样例

<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.chw.demo</groupId>
    <artifactId>jd2107-webtest-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- 选择让项目使用jdk11 -->
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <!-- 打包后项目的名字,可以使用变量表示 -->
        <!-- 项目坐标中的artifactId标签只,就是打包后jar名字 -->
        <finalName>${project.artifactId}</finalName>
        <!-- 描述项目中资源的位置 -->
        <resources>
            <!-- 自定义资源,可以有多个自定义资源文件夹 -->
            <resource>
                <!-- 资源目录 -->
                <directory>src/main/resources</directory>
                <!-- 包括哪些文件参与打包 -->
                <includes>
                    <!-- **/*代表所有目录的所有子文件都包括 -->
                    <include>**/*</include>
                </includes>
                <!-- 排除哪些文件不参与打包 -->
                <excludes>
                    <exclude>config/*</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins>
            <!-- 可以配置多个插件,要什么自己去找,并且插件会有相关参数 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <!-- 配置tomcat插件,可以不要,因为我们自己有安装tomcat -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <!-- 设置web项目的根目录为/ -->
                    <path>/</path>
                    <!-- 设置web项目的端口号为8989 -->
                    <port>8989</port>
                    <!-- 设置编码为UTF-8 -->
                    <uriEncoding>UTF-8</uriEncoding>
                </configuration>
            </plugin>
            <!-- 源码打包插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <!-- 绑定source插件到Maven的生命周期 (compile) -->
                        <phase>compile</phase>
                        <!--在生命周期后执行绑定的source插件的goals(jar-no-fork) -->
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- 可执行jar插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <configuration>
                    <!-- 设置打包时候生成dependency-reduced-pom.xml文件为false -->
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <!-- 绑定shade插件到Maven的生命周期 (package) -->
                        <phase>package</phase>
                        <!--在生命周期后执行绑定的shade插件的goals -->
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <!--指定程序入口类 -->
                                    <mainClass>com.chw.MainApplication</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

5.2.1、配置maven

在开发工具中使用maven时,要配置关联maven、仓库信息、下载内容信息等

5.2.2、maven项目

File中new一个maven project,环境黄色警告可以自己修改,右键JRE System Library,选其首选项,修改成对应的JRE和JDK即可

5.2.2.1、更新maven项目

Alt+F5,使用Maven中的Update Project

5.2.2.2、maven测试,不是junit测试

右键项目 -> Run As -> Maven test

5.2.2.3、使用maven命令

  • 右键项目 -> Run As -> Maven build 写goals类似clean package等

  • 在里面输入命令,不需要写mvn,还可以勾选一些方法,比如跳过测试

  • 还可以删除历史执行命令:右键项目 -> Run As -> Run Configurations

  • 也可以show in -> 想展示的地方

5.2.2.4、在新项目中使用旧maven项目

  • 但是容易产生冲突,例如A中引用了junit4,并且依赖范围是可传递的,那么当B引用A并且引用junit5,这样就产生冲突了

  • 但是maven会帮我们管理,选择最短路径的依赖

<dependency>
  <groupId>com.chw.demo</groupId>
  <artifactId>hello</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>

5.2.2.5、在properties标签中可以定义变量,然后${变量名}使用

<properties>
  <!-- 这三个变量,其实使用让maven默认自带的插件使用的 -->
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
  
  <!-- 指定hello项目的版本(变量) -->
  <hello.version>0.0.1-SNAPSHOT</hello.version>
</properties>
​
<dependencies>
  <dependency>
    <groupId>com.chw.demo</groupId>
    <artifactId>hello</artifactId>
    <!-- 使用上面定义的变量 -->
    <version>${hello.version}</version>
  </dependency>
</dependencies>

5.2.2.6、依赖

  • 依赖有直接依赖和间接依赖,依赖可以传递

  • 在hello项目中,直接依赖了junit-4.13.jar,而junit-4.13.jar又依赖了hamcrest-core-1.3.jar,那么hello项目就间接依赖了hamcrest-core-1.3.jar (依赖传递)

5.2.2.6.1、scope范围及依赖是否传递
  • compile ,默认值,适用于所有阶段(开发、测试、部署、运行),jar包会一直存在所有阶段 会依赖传递

  • provided ,只在开发、测试阶段使用,目的是不让将来部署的容器和你本地仓库的jar包冲突,因为有些jar包在将来部署的服务器或者容器中是存在的,所以打包的是就不需要把这些jar添加进来了,如servlet相关jar包 不会依赖传递

  • runtime ,只在运行时使用,如JDBC驱动,适用运行和测试阶段。 会依赖传递

  • system ,类似provided,需要显式提供包含依赖的jar的路径,Maven不会在Repository中查找它 不会依赖传递

  • test ,只在测试时使用,用于编译和运行测试代码。不会随项目发布,如JUnit相关jar包 不会依赖传递

5.2.2.6.2、依赖排除
<dependency>
  <groupId>com.chw.demo</groupId>
  <artifactId>hello</artifactId>
  <version>${hello.version}</version>
  
  <!-- 排除依赖传递,这里不用指定版本,因为它是确定的 -->
  <exclusions>
    <exclusion>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </exclusion>
  </exclusions>
</dependency>

5.2.3、maven项目的生命周期

项目在Maven中,有三套相互独立的生命周期,这三套生命周期分别是:

  • Clean Lifecycle,在进行真正的构建之前进行一些清理工作

  • Default Lifecycle,构建的核心部分,编译,测试,打包,测试、安装、部署等等

  • Site Lifecycle,生成项目报告,站点,发布站点

5.2.3.1、phase

这三套生命周期中,又分别包含了若干个阶段(phase),每一个阶段(phase)可以理解为一个步骤,这些步骤按照既定好的顺序执行,从而完成一个项目的构建

  • Clean Lifecycle,分为以下几个阶段(phase)

  1. pre-clean 执行一些需要在clean之前完成的工作

  2. clean 移除所有上一次构建生成的文件(常用)

  3. post-clean 执行一些需要在clean之后立刻完成的工作

  • Default Lifecycle,分为以下几个阶段(phase)

  1. validate

  2. generate-sources

  3. process-sources

  4. generate-resources

  5. process-resources 复制并处理资源文件,至目标目录,准备打包

  6. compile 编译项目的源代码 (常用)

  7. process-classes

  8. generate-test-sources

  9. process-test-sources

  10. generate-test-resources

  11. process-test-resources 复制并处理资源文件,至目标测试目录

  12. test-compile 编译测试源代码 (常用)

  13. process-test-classes

  14. test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署 (常用)

  15. prepare-package

  16. package 接受编译好的代码,打包成可发布的格式,如 jar war等 (常用)

  17. pre-integration-test

  18. integration-test

  19. post-integration-test

  20. verify

  21. install 将包安装至本地仓库,以让其它项目依赖。 (常用)

  22. deploy

  • Site Lifecycle,分为以下几个阶段(phase)

  1. pre-site 执行一些需要在生成站点文档之前完成的工作

  2. site 生成项目的站点文档

  3. post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备

  4. site-deploy 将生成的站点文档部署到特定的服务器上

5.2.3.2、常用命令

  • mvn clean

  • mvn compile

  • mvn test-compile

  • mvn test

  • mvn package

  • mvn install

  • mvn tomcat7:run

  • mvn spring-boot:run

5.2.4、plugin

  • 生命周期中,各个步骤/阶段(phase)的工作,是由相应的插件(plugin)来完成的,不同的插件结合起来,就可以完成整个项目的构建

  • 一个插件通常完成一个或多个步骤的工作,每个步骤的工作对应插件的一个目标(goal)。

  • 默认情况下,我们不需要任何插件配置,Maven中就已经设置好了不同的插件,来处理相应的步骤

  • maven官方插件https://maven.apache.org/plugins/

//tomcat插件
<plugin>
  <groupId>org.apache.tomcat.maven</groupId>
  <artifactId>tomcat7-maven-plugin</artifactId>
  <version>2.2</version>
  <configuration>
    <path>/</path>
    <port>8989</port>
    <uriEncoding>UTF-8</uriEncoding>
  </configuration>
</plugin>

5.2.5、maven、生命周期(lifecycle)、阶段(phase),插件(plugin)、目标(goal)间的关系

  • 一个maven项目有三个生命周期,一个生命周期对应多个阶段,

  • 一个插件有一个或多个目标

  • 一个目标对应完成一个阶段

5.2.6、build标签

在pom文件中的 <build>标签中,可以配置打包后jar的名字、项目中资源文件的位置、配置构建时候的插件等

5.2.7、继承

  • 有jar(通用)、war(web项目)、pom(方便子项目继承)

  • 父项目是创建Maven Project,是pom打包方式

  • 子项目可以是Maven Project,但如果是Maven Module就一定是子项目,然后就去使用父项目就行,子项目只需要在parent外写 artifactId 这个就行,其他的父项目中都有写,并且使用的时候不需要给版本,只需要使用就行<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies>

  • 即只需要写groupId和artifactId就行,其余的都使用父项目的

  • 也可以直接在父项目中new Maven Module

  • 子项目一定是被父项目包含的,就算是新创建Maven Project,看起来是平行目录,但其实是包含关系

  • 使用父项目方便管理,父项目只需要一个pom.xml

  • 一般打包是去打包父项目,因为父项目中的modules,可以把所有的子项目都一块打包,这个modules是创建子模块的时候自动创建的

<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.chw.demo</groupId>
    <artifactId>mvn-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <junit.version>4.13</junit.version>
    </properties>
    <!-- dependencyManagement标签,用来管理依赖,并不是直接给子项目引入 -->
    <!-- 子项目中可以自己去引入使用这里管理的依赖,不需要配置版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <finalName>${project.artifactId}</finalName>
        <pluginManagement>
            <plugins>
            </plugins>
        </pluginManagement>
    </build>
​
    <modules>
        <module>mvn-child</module>
    </modules>
</project>

5.2.8、聚合

  • 聚合项目,一般是将一个项目拆分成多个模块项目,每个模块是一个独立的工程,负责完成一个独立的任务,但是在运行的时候,必须把所有模块项目聚合到一起,才是一个完整的工程

  • 先创建一个聚合项目,此项目类似于之前的父项目,打包方式一定要为pom

  • 即父项目提供pom,子模块实现独立业务

  • 父项目里面有modules,然后父项目需要把各个子项目放到依赖里面,这样就可以实现各个子项目之间的互相引用了

<dependency>
    <groupId>com.chw.smart</groupId>
    <artifactId>env-gather-entity</artifactId>
    <version>${env.gather.version}</version>
</dependency>
​
<dependency>
    <groupId>com.chw.smart</groupId>
    <artifactId>env-gather-interface</artifactId>
    <version>${env.gather.version}</version>
</dependency>

5.2.9、Working Set

  • 用来归类项目的

  • 三个竖点,然后点Select Working Set,就可以创建或者选择工作集了

  • 三个竖点,然后点Edit Active Working Set,就可以选择什么项目是什么工作集了

5.2.10、自定义Plugin开发(插件)

  • 进入maven官网,然后进入Plugin Developer Centre

  • 引入接口后,将自己写的代码当作插件

  • 写一个Mojo,给他定义目标,然后重写个execute方法

  • 我们自定义的插件和官方给的插件的运行方式、打包方式等都有些不一样

  • 项目就是一个插件,我们需要修改打包方式,引入依赖后实现目标

  • 通过实现别人的接口、使用注解等方法,就能接入别人的体系了

5.2.10.1、开发步骤

  1. 使用插件的打包方式maven-plugin,如果报错,就选择第二个,忽略,然后它会帮你自动生成一个插件,这个插件是用来配置我们生命周期的映射的,不以为是没用的就直接删掉了

<groupId>com.chw.demo</groupId>
<artifactId>jd2107-webtest-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <packaging>war</packaging> -->
<packaging>maven-plugin</packaging>
  1. 复制两个依赖,api、annotations(注解)

<dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-plugin-api</artifactId>
    <version>3.0</version>
    <scope>provided</scope>
</dependency>
<!-- dependencies to annotations -->
<dependency>
    <groupId>org.apache.maven.plugin-tools</groupId>
    <artifactId>maven-plugin-annotations</artifactId>
    <version>3.4</version>
    <scope>provided</scope>
</dependency>
  1. 继承AbstractMojo,使用@Mojo注解,重写execute方法

@Mojo(name = "sayhi")
public class HelloPlugin extends AbstractMojo {
    @Override
    public void execute() throws MojoExecutionException {
        getLog().info("Hello, world.");
    }
}
  1. 引用自定义插件,就在pom文件的插件plugin通过groupId、artifactId、version来引用,但是记住,要加个执行标签,即挂载点,<executions>,里面写阶段和目标,然后<execution>报错的话就选第二个

  <build>
    <plugins>
      <plugin>
        <groupId>com.chw.demo</groupId>
        <artifactId>jd2107-webtest-01</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  1. 最好把插件下载到本地:右键项目->Run As->Maven install,然后通过5Maven build... goals写上阶段(compile)

  2. 传参,在插件中加上<configuration>标签,里面写的就算自定义的变量名标签,使用的时候加注解@Parameter

<groupId>com.chw.demo</groupId>
        <artifactId>jd2107-webtest-01</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <configuration>
            <name>jack</name>
        </configuration>
<executions><execution><phase>compile</phase><goals>
<goal>sayhi</goal></goals></execution></executions>

6、ServerSocket服务器

6.1、使用服务器去关闭另一个服务器

  • 新增一个端口,给专门的服务器,让其去监听客户端是否想要关闭服务器

  • 这样就会有A服务器专门处理业务,accept阻塞等待客户端的业务调用,B服务器专门监听关闭业务,accept阻塞等待客户端的关闭指令

  • 使用服务器去关闭服务器的原因是,你不可能在服务器里面关闭,因为是要客户端说想关了才能关,但你也不可能让客户端去使用服务器的内部细节

  • 主要的原因是因为不写死,想关就关

6.2、volatile使用场景

  • 使用场景是:两个或多个线程访问同一个堆内变量时

  • 堆里的needStop变量,A、B线程都要去访问

  • 一般而言,JVM会允许线程复制变量拿到栈里去使用,更高效,但是为了线程同步,我们希望线程每次取变量的时候是去取堆内变量,而不是复制一份到栈中使用,这样的话就不会有B线程改变变量但是A线程仍然不变的情况了

  • volatile可以强制让线程每次都去堆里面拿,以免出错

  • 如果使用synchronized的话,这个关键字太霸道了,消耗太大,我们只想要轻量级的volatile

7、Log4j

  • 轻量的日志管理框架,4是for的意思

  • 开发调试日志、系统运行日志

7.1、级别

  • 级别从高到低:OFF、 FATAL、ERROR、WARN、INFO、DEBUG、TRACE、ALL或用户自定义的级别

  • 程序会打印高于或等于所 设置级别的日志,设置的日志等级越高,打印出来的日志就越少,修改log4j.properties里面的log4j.rootLogger=debug,contrl,file的debug,改成别的级别

  • 当输出低等级的日志时候,相对于此等级的高等级也会全都输出

7.2、log4j对比System.out.println优势

  1. 分级别控制日志的输出与开关 调试阶段写了100system语句,上线时需不需要这些语句?

(不需要手动删除,自己就能不输出)

  1. 按不同的类和包进行输出控制 很多模块都有输出语句,但是不能很好区分是哪一个输出?

(不需要区分,可以根据不同的包,不同的类分情况输出)

  1. 控制日志不同的输出位置

7.3、定义log4j.properties配置文件

  1. 配置根Logger

  2. 配置日志信息输出目的地 Appender,Appender 负责控制日志记录操作的输出

  3. 配置日志信息的格式(布局)Layout,Layout 负责格式化Appender的输出

7.3.1、格式化日志信息

Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:

  • %m 输出代码中指定的消息

  • %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL

  • %r 输出自应用启动到输出该log信息耗费的毫秒数

  • %c 输出所属的类目,通常就是所在类的全名

  • %t 输出产生该日志事件的线程名

  • %n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”

  • %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

  • %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。

8、开发中常见的问题

  • 修改session、aplication

8.1、日期格式问题

8.1.1、将数据库中的数据在后端转换

8.1.1.1、在jsp页面中使用的方式

  1. 在实体类中定义getter方法,然后在遍历对象的时候使用这个方法得到时间

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String date = sdf.format(mydate);
  1. 使用jstl的 fmt 标签库,

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="f"%>
​
<li>出版时间:<f:formatDate value="${book.pubDate}"
                 pattern="yyyy-MM-dd" /></li>
  1. 8.2、表单提交问题

8.2.1、表单提交方式

  1. input普通方式

  2. onclick方式,方便添加样式

<script type="text/javascript">
  function submitForm() {
      document.getElementById("addCar").submit();
  }
</script>
​
<div class="pro_addshop" οnclick="submitForm()">加入购物车</div>
  1. href方式,方便添加样式

<div class="pro_addshop">
    <a href="javascript:submitForm()">加入购物车</a>
</div>

8.3、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值