Java基础知识(三)(文末含数据库驱动jar包,C3P0的jar包和Druid的jar包,JDBCTemplate的jar包)

Java基础知识(三)

1.Junit单元测试

  • 单元测试:
    1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值
    2.白盒测试:需要写代码的。关注程序具体的执行流程

Junit使用:白盒测试
步骤:

  1. 定义一个测试类(测试用例)
    建议:
    测试类名:被测试的类名Test
    包名:×××.×××.××.test
  2. 定义测试方法:可以独立运行
    建议:
    方法名:test测试的方法名
    返回值:void
    参数列表:空参
  3. 给方法加@Test
  4. 导入junit依赖环境

判定结果:

  • 绿色:成功
  • 红色:失败
  • 一般会使用断言操作来处理Assert.assertEquals(期望结果,result);

补充

  • @Before修饰的方法会在测试方法之前被自动执行
  • @After修饰的方法会在测试方法执行完后被自动执行
package newday.junit;

public class Calculator {
    /**
     * 加法
     * @param a
     * @param b
     * @return
     */
    public int add(int a,int b){
        //int i=3/0;
        return a-b;
    }
    /**
     * 减法
     * @param a
     * @param b
     * @return
     */
    public int sub(int a,int b){
        return a-b;
    }
}

package newday.test;

import newday.junit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {
    /**
     * 初始化方法;
     * 用于资源申请,所有测试方法在执行前都会执行该方法
     */
    @Before
    public void init(){
        System.out.println("init...");
    }
    /**
     * 释放资源方法;
     * 在所有测试方法执行完后,都会自动执行该方法
     */
    @After
    public void close(){
        System.out.println("close...");
    }
    /**
     * 测试add方法
     */
    @Test
    public void testAdd(){
        //System.out.println("我被执行啦");
        //创建对象
        Calculator c=new Calculator();
        //调用方法
        int result=c.add(2,3);
        //System.out.println(result);
        System.out.println("testAdd...");
        //我断言这个结果是5
        Assert.assertEquals(5,result);
    }
    @Test
    public void testSub(){
        //System.out.println("我被执行啦");
        //创建对象
        Calculator c=new Calculator();
        //调用方法
        int result=c.sub(2,3);
        System.out.println("testSub...");
        //我断言这个结果是5
        Assert.assertEquals(-1,result);
    }
}

2.反射:框架设计的灵魂

  • 框架:半成品软件。可以在框架的基础上进行软件开发,简化代码
  • 反射概念:将类的各个组成部分封装为其他对象,这就是反射机制

Java代码在计算机中经历的三个阶段:
1.Source 源代码阶段
2.Class 类对象阶段
3.Runtime 运行时阶段

  • 反射好处:
    1.在程序的运行过程中,操作这些对象
    2.可以解耦,来提高程序的可扩展性
  • 获取Class对象的方式:
    1.Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象;多用于配置文件,将类名定义在配置文件中;读取文件,加载类
    2.类名.class:通过类名的属性class获取;多用于参数的传递
    3.对象.getClass():getClass()方法在object类中定义着;多用于对象的获取字节码的方式
public class ReflectDemo1 {
    /*
    获取Class对象的方式:
        1.Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        2.类名.class:通过类名的属性class获取
        3.对象.getClass():getClass()方法在object类中定义着
     */
    public static void main(String[] args) throws ClassNotFoundException {
        //1.Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        Class cls1 = Class.forName("newday.domain.Person");
        System.out.println(cls1);
        //2.类名.class:通过类名的属性class获取
        Class cls2= Person.class;
        System.out.println(cls2);
        //3.对象.getClass():getClass()方法在object类中定义着
        Person p=new Person();
        Class cls3=p.getClass();
        System.out.println(cls3);
        //==去比较这三个对象
        System.out.println(cls1==cls2);//true
        System.out.println(cls1==cls3);//true
    }
}

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

  • Class对象功能:
    获取功能:
    1.获取成员变量们
    Field[] getFields() 获取所有public修饰的成员变量
    Field getField(String name) 获取指定名称的public修饰的成员变量
    Field[] getDeclaredFields() 获取所有成员变量,不考虑修饰符
    Field getDeclaredField(String name)获取指定名称的成员变量
    2.获取构造方法们
    Constructor<?>[] getConstructors() Constructor<T> getConstructor(Class<?>... parameterTypes)
    Constructor<?>[] getDeclaredConstructors() Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    3.获取成员方法们
    Method[] getMethods() Method getMethod(String name, Class<?>... parameterTypes)
    Method[] getDeclaredMethods() Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    4.获取类名
    String getName()
public class Person {
    private String name;
    private int age;
    public String a;
    protected String b;
    String c;
    private String d;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
    public void eat(){
        System.out.println("eat...");
    }

    public void eat(String food){
        System.out.println("eat..."+food);
    }
}

  • Field:成员变量
    1.设置值void set(Object obj, Object value)
    2.获取值get(Object obj)
    3.忽略访问权限修饰符的安全检查setAccessible(true);暴力反射
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class<Person> personClass = Person.class;
    /*
    1.获取成员变量们
     `Field[] getFields()`   `Field getField(String name)  `
     `Field[] getDeclaredFields()`   ` Field getDeclaredField(String name)  `
     */
        //Field[] getFields() public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("-------------");
        //Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);
        System.out.println("----------------");
        //Field[] getDeclaredFields() 所有成员变量
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        //Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);//私有会报错,访问之前忽略访问权限修饰符的安全检查
        System.out.println(value2);
    }
}
  • Constructor:构造方法
    1.创建对象:T newInstance(Object... initargs)
    如果构造使用空参数方法创建对象,操作可以简化:Class对象的newInstance方法
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class<Person> personClass = Person.class;
    /*
    2.获取构造方法们
 ` Constructor<?>[] getConstructors() ` `Constructor<T> getConstructor(Class<?>... parameterTypes)  `
 ` Constructor<?>[] getDeclaredConstructors()  ` ` Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) `
     */
        //Constructor<T> getConstructor(Class<?>... parameterTypes
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);
        Person person1 = personClass.newInstance();
        System.out.println(person1);
        //constructor.setAccessible(true);
    }
}
  • Method:方法对象
    执行方法:Object invoke(Object obj, Object... args)
    获取方法名称:String getName获取方法名
public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //0.获取Person的Class对象
        Class<Person> personClass = Person.class;
    /*
    3.获取成员方法们
` Method[] getMethods()  ` `Method getMethod(String name, Class<?>... parameterTypes)  `
`Method[] getDeclaredMethods()` `Method getDeclaredMethod(String name, Class<?>... parameterTypes) `
     */
        //获取方法名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p=new Person();
        //执行方法
        eat_method.invoke(p);

        Method eat_method2 = personClass.getMethod("eat", String.class);
        eat_method2.invoke(p,"饭");

        System.out.println("-----------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);//包括了object方法
            //method.setAccessible(ture);
            String name = method.getName();
            System.out.println(name);
        }
        /*
        4.获取类名
        `String getName() `
         */
        String classname = personClass.getName();
        System.out.println(classname);//全类名
    }
}
  • 案例
    需求:写一个“框架”,不能改变该类的任何代码的前提下,可以帮助我们创建任意类的对象,并且执行其中任意方法
    实现需要:1.配置文件 2.反射
    步骤:
    1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2.在程序中加载读取配置文件
    3.使用反射技术来加载类文件进内存
    4.创建对象
    5.执行方法
className=newday.domain.Person
methodName=eat
//如需更改只需更改配置文件⬆
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法
        /*
            前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */
        /*Person p= new Person();
        p.eat();*/
        /*Student p=new Student();
        p.sleep();*/
        //加载配置文件
        //1.创建Properties对象
        Properties pro=new Properties();
        //2.加载配置文件,转换为一个集合
        //2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

        //获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //加载该类进内存
        Class cls = Class.forName(className);

        //创建对象
        Object obj = cls.newInstance();

        //获取方法对象
        Method method = cls.getMethod(methodName);

        //执行方法
        method.invoke(obj);
    }
}

3.注解

  • 概念:说明程序。给计算机看
    注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释
    使用注解:@注解名称

  • 作用分类:
    ①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】(命令行 javadoc 名.java 生成文件)
    ② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
    ③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

  • JDK中预定义的注解
    @Override:检测被该注解标注的方法是否是继承自父类
    @Deprecated:该注解标注的内容,表示已过时
    @SuppressWarnings:压制警告,一般传递参数all @SuppressWarnings("all")

  • 自定义注解

    • 格式:
      元注解
      public @interface 注解名称{属性列表;}
    • 本质:就是一个接口,该接口默认继承Annotation接口
      public interface MyAnno extends java.lang.annotation.Annotation {}
    • 属性:接口中的抽象方法
      • 要求:
        • 属性的返回值类型只可以取值:基本数据类型,String,枚举,注解,以上类型的数组
        • 定义了属性,在使用时需要给属性赋值:
          1.如果定义属性时,使用default关键字给属性默认初始值,则使用注解时,可以不进行属性的赋值
          2.如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
          3.数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}省略
    • 元注解:描述注解的注解
    1. @Target:描述注解能够作用的位置
      ElemetType取值:TYPE:可以作用于子类上;METHOD:可以作用于方法上;FIELD:可以作用于成员变量上
    2. @Retention:描述注解被保留的一个阶段 @Retention(RetentionPolicy.RUNTIME):当前被描述的注解会保留到clss字节码文件中,并被JVM读取到
    3. @Documented:描述注解是否被抽取到api文档中
    4. @Inherited:描述注解是否被子类继承
@Target(value={ElementType.TYPE})//表示MyAnno3注解只能作用于类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno3 {
}

  • 在程序中使用(解析)注解:获取注解中定义的属性值
    1. 获取注解定义的位置的对象(Class,Method,Field…)
    2. 获取指定的注解 getAnnotation(Class)
    3. 调用注解中的抽象方法获取配置的属性值
/**
 * 描述需要去执行的类名和方法名,和方法名
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface pro {
    String className();
    String methodName();
}

@pro(className = "newday.domain.Person",methodName = "eat")
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法
        /*
            前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
         */

        //解析注解
        //1.获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        //2.获取注解对象
        //其实就是在内存中去生成了一个该注解接口的子类实现对象
        /*
            public class proImp implements pro{
                public String className(){
                    return "newday.domain.Person";
                }
                public String methodName(){
                    return "show";
                }
            }
         */
        pro an = reflectTestClass.getAnnotation(pro.class);
        //3.调用注解对象中定义的抽象方法,获取返回值
        String className = an.className();
        String methodName = an.methodName();

        //加载该类进内存
        Class cls = Class.forName(className);

        //创建对象
        Object obj = cls.newInstance();

        //获取方法对象
        Method method = cls.getMethod(methodName);

        //执行方法
        method.invoke(obj);
    }
}

以后大多数时候,我们会使用注解,而不是自定义注解
注解给谁用?1.编译器 2.解析程序
注解不是程序的一部分,可以理解为注解就是一个标签

4.数据库(DataBase 简:DB)

4.1基本概念

  • 概念:用于存储和管理数据的仓库
  • 特点:
    1. 持久化存储数据。其实数据库就是一个文件系统
    2. 方便存储和管理数据
    3. 使用了统一的方式操作数据库 -----SQL
  • 常见的数据库软件

4.2 MySQL数据库软件

管理员权限打开cmdnet stop mysql net start mysql

  • MySQL登录
    1. mysql -uroot -p密码
    2. mysql -hip -uroot -p连接目标的密码
    3. mysql --host=ip --user=root --password=密码
  • MySQL退出
    1. exit
    2. quit
  • MySQL目录结构
    1. MySQL安装目录
    2. MySQL数据目录

5.SQL

  • 什么是SQL
    Structured Query Language:结构化查询语言。其实就是定义了操作所有关系型数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。
  • SQL通用语法
    1. SQL语句可以单行或多行书写,以分号结尾。
    2. 可使用空格和缩进来增强语句的可读性。
    3. MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。
    4. 三种注释:单行注释:-- 注释内容#注释内容(mysql特有);多行注释:/*注释内容*/
  • SQL分类:
    DDL(数据定义语言):操作数据库 、表;
    DML(数据操作语言):增删改表中数据;
    DQL(数据查询语言):查询表中数据;
    DCL(数据控制语言):管理用户,授权

5.1 DDL(操作数据库 、表)

  • 操作数据库:CRUD
    1. C(Create):创建
    创建数据库:create database 数据库名称
    创建数据库并判断不存在再创建:create database if not exists
    创建数据库并指定字符集:craete database 数据库名称 character set 字符集名
    eg:创建db1数据库,判断是否存在,并指定字符集是gbk create database if not exists db1 set gbk
    2. R(Retrieve):查询
    查询所有数据库的名称:show databases
    查看某个数据库的字符集:查询某个数据库的创建语句show create database 数据库名称
    3. U(Updata):修改
    修改数据库的字符集:alter database 数据库名称 character 字符集名称
    4. D(Delete):删除
    删除数据库:drop database 数据库名称 drop if exists 数据库名称
    5. 使用数据库
    查询当前正在使用的数据库名称:select database()
    使用数据库:use 数据库名称

  • 操作表
    1. C(Create):创建
    语法:create table 表名(列名1 数据类型,列名2 数据类型,列名3 数据类型,...,列名n 数据类型);
    数据类型(常见):
    1.int
    2.double
    3.date:年月日
    4.datetime:年月日时分秒
    5.timestamp:时间戳类型包含年月日时分秒,如果不给这个字段赋值或赋值为null,则默认使用当前的系统时间,自动赋值
    6.varchar:字符串
    复制表:crate table 表名 like 被复制的表名
    2. R(Retrieve):查询
    查询某个数据库中所有表名称 :show tables
    查询表结构:desc 表名
    3. U(Updata):修改
    修改表名:alter table 表名 rename to 新的表名
    修改表的字符集:alter table 表名 character set 字符集名称
    添加一列:alter table 表名 add 列名 数据类型
    修改列名称 类型:alter table 表名 change 列名 修改新列名 新数据类型 alter table 表名 modify 列名 新数据类型
    删除列:alter table 表名 drop 列名
    4. D(Delete):删除
    drop table 表名 drop table if exists 表名

5.2 DML(增删改表中数据)

  • 添加数据
    语法:insert into 表名(列名1,列名2,...,列名n) values (值1,值2,...,值n)
    注意:列名和值一一对应;如果表名后不定义列名,则给所有列添加值;除数字类型,其他类型需要使用引号(单双都可以)引起来
  • 删除数据
    语法:delete from 表名 [where 条件]
    注意:如果不加条件,则删除所有记录,但不建议使用(有多少条记录则执行多少次操作,效率低)
    建议使用truncate table 表名(先删除表,再创建一个一样的表,只执行两条操作,效率高)
  • 修改数据
    语法:update 表名 set 列名1=值1,列名2=值2,... [where 条件]
    注意:如果不加任何条件,则修改所有记录

5.3 DQL(查询表中数据)

5.3.1 单表查询
  • 语法:
select
	字段列表
from
	表名列表
where
	条件列表
group by
	分组字段
having
	分组之后的条件
order by
	排序
limit
	分页限定
  • 基础查询
    1. 多个字段的查询:select 字段名1,字段名2,字段名3,... from 表名 selct * from 表名
    2. 去除重复:select distinct 字段名 from 表名
    3. 计算列
    一般可以使用四则运算计算一些列的值(一般只会进行数值型的计算),null参与运算结果都为努力了
    ifnull(表达式1,表达式2)表达式1:哪个字段需要判断是否为null 表达式2:如果该字段为null的替换值
    4. 起别名:asas也可以省略

  • 条件查询
    1. where子句后跟条件
    2. 运算符
    > , < , >= , <= ,= , <>(!=) BETWEEN...AND IN(集合) IS NULL(null不能使用=判断) and 或 && or 或 || not 或 !
    LIKE 模糊查询:占位符:_:单个任意字符 %:多个任意字符
    举个🙋:
    查询姓马的人:select * from 表名 where 字段名 like ‘马%’
    查询第二个字为化的人:select * from 表名 where 字段名 like ‘_化%’
    查询姓名为3个字的人:select * from 表名 where 字段名 like ‘___’
    查询包含马的人:select * from 表名 where 字段名 like ‘%马%’

  • 排序查询
    语法:order by 排序字段1 排序方式1,排序字段2 排序方式2,...
    排序方式:ASC:升序,默认升序;DESC:降序
    注意:如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件

  • 聚合函数:将一列数作为一个整体,进行纵向的计算
    1. count:计算个数(一般选择非空的列,主键)count(字段名)
    2. max:计算最大值
    3. min:计算最小值
    4. sum:计算和
    5. avg:计算平均值
    注意:聚合函数的计算会排除null值
    解决方案:选择不包含null的列进行计算;IFNULL函数

  • 分组查询
    语法:group by 分组字段
    注意:
    分组之后查询的字段,分组字段,聚合函数;
    where和having的区别:
    1.where再分组之前进行限定,如果不满足条件,则不参与分组;having在分组之后进行限定,如果不满足结果,则不会被查询出来
    2.where后不可以跟聚合函数,having可以进行聚合函数的判断
    举个例子💖select sex, avg(math), count(id) 人数 from stu where math>70 group by sex having count(id)>2

  • 分页查询(limit是mysql的“方言”)
    语法:limit 开始的索引, 每页查询的条数 开始的索引=(当前的页码-1)* 每页显示的条数

5.3.2 多表查询
  • 笛卡尔积:
    有两个集合A,B,取这两个集合的所有组成情况。乘积
    要完成多表查询,需要消除无用的数据
  • 内连接查询
    1. 隐式内连接:使用where条件消除无用数据
    举个例子🍬
    select * from emp.gender, dept.name from emp,dept where emp.dept_id=dept_id
    select t1.name, t2.name from enp t1,dept t2 where t1.dept_id=t2.id
    2. 显示内连接
    语法:select 字段列表 from 表名1 inner join 表名2 on 条件
    3. 注意:从哪些表中查数据;条件是什么;查询哪些字段
  • 外连接查询
    1. 左外连接:
    语法:select 字段列表 from 表1 left outer(可省略) join 表2 on 条件
    查询的是左表所有数据以及其交集部分
    2. 右外连接:
    语法:select 字段列表 from 表1 right outer(可省略) join 表2 on 条件
    查询的是右表所有数据以及其交集部分
  • 子查询
    概念:查询中嵌套查询,称嵌套查询为子查询
    子查询的不同情况
    1. 子查询的结果是单行单列的:子查询可以作为条件,使用运算符计算
    2. 子查询的结果是多行单列的:子查询可以作为条件,使用运算符IN来判断
    3. 子查询的结果是多行多列的:子查询可以作为一张虚拟表参与查询

5.4 DCL(数据控制语言)

DBA(数据库管理员)负责

  • 管理用户
    1. 添加用户create user '用户名'@'主机名' identified by '密码'
    2. 删除用户drop user '用户名'@'主机名'
    3. 修改用户密码set password '用户名'@'主机名'=password('新密码')
    MySQL中忘记了root的密码:
    *cmd—>net stop mysql 停止MySQL的服务(管理员身份运行cmd)
    *使用无验证方式启动MySQL服务:mysqld --skip-grant-tables
    *打开新的cmd窗口,直接输入mysql命令,敲回车,就可以登录成功
    *use mysql update user set password=password('root') where user='root'
    *关闭两个窗口
    *打开任务管理器,手动结束mysqld.exe的进程
    *启动mysql服务
    *使用新密码登录
    4. 查询用户:
    切换到MySQL数据库:use mysql 查询user表:select * from user
    通配符:%表示可以在任意主机使用用户登录数据
  • 管理权限
    1. 查询权限show grants for '用户名'@'主机名'
    2. 授予权限grant 权限列表 on 数据库名.表名 to '用户名'@'主机名' 授予所有grant all on *.* to '用户名'@'主机名'
    3. 撤销权限revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名'

5.5 约束

  • 概念:对表中数据进行限定,保证数据的正确性、有效性、完整性。
  • 分类:
    1. 主键约束:primary key
    删除主键:alter table 表名 drop primary key
    自增:auto_increment可以完成值的自动增长
    2. 非空约束:not null
    3. 唯一约束:unique MySQL中,唯一约束限定的列的值可以有多个null
    删除唯一约束:alter table 表名 drop index 列名
    4. 外键约束:foreign key让表与表产生一些关系,从而保证数据的正确性
    创建:create table 表名(... constraint 外键名称 foreign key (外键列名称) reference 主表名称(主表列名称)
    删除:alter table 表名 drop foreign key 外键名称
    添加外键:alter table 表名 add constraint 外键名称 foreign key (外键列名称) reference 主表名称(主表列名称)
    级联操作:更新:on update cascade 删除:on delete cascade

6.数据库设计

  • 多表之间的关系
    1. 一对一(了解)
    2. 一对多(多对一)
    在多的一方建立外键,指向一的一方的主键
    3. 多对多
    需要借助第三张中间表
  • 数据库设计的范式
    1. 第一范式(1NF):每一列都是不可分割的原子数据项
    2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于候选码(1NF基础上消除非主属性码对主码的部分函数依赖)
    几个概念:
    函数依赖:A—>B,如果通过A属性(属性组)的值,可以确定唯一B的属性值。则称B依赖于A
    完全依赖:A—>B,如果A是一个属性组,则B属性值的确定需要依赖于A属性组中的所有的属性值
    部分函数依赖:A—>B,如果A是一个属性组,则B属性值的确定只需要依赖于A属性组中的某一些属性值即可
    传递函数依赖:A—>B,B—>C,如果通过A属性(属性组)的值,可以确定唯一B的属性,在通过B属性(属性组)的值可以确定唯一C属性的值,则称C传递依赖于A
    :如果在一张表中,一个属性或属性组,被其他所有属性完全依赖,则称这个属性(属性值)为该表的码
    主属性:码属性组中所有属性
    非主属性:除了码属性组的属性
    3. 第三范式(3NF):在2NF的基础上,任何非主属性不依赖于其他非主属性(在2NF的基础上消除传递依赖)

7.数据库的备份和还原

  1. 命令行
    备份语法:mysqldump -u用户名 -p密码 > 保存的路径
    还原:登录数据库;创建数据库;使用数据库;执行文件:source 文件路径
  2. 图形化工具

8.事物

  • 概念:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败
  • 操作:
    1. 开启事务:start transaction
    2. 回滚:rollbake
    3. 提交:commit
    4. 事物提交的两种方式
    自动提交:MySQL数据库中事物默认自动提交;一条DML语句会自动提交一次事物
    手动提交:需要先开启事物再提交;Oracle数据库默认是手动提交事物
    修改事物的默认提交方式:
    查看事物的默认提交方式select @@autocommit1代表自动提交,0代表手动提交;修改:set @@autocommit=0
  • 事物的四大特征
    1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败
    2. 持久性:当事物提交或回滚后,数据库会持久化的保存数据
    3. 隔离性:多个事物之间,相互独立
    事物的隔离级别(了解):
    概念:
    多个事务之间隔离,相互独立。但是如果多个事物操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
    存在问题:
    *脏读:一个事物读取到另一个事物中没有提交到的数据;
    *不可重复读(虚读):在同一个事务中两次读取到的数据不一样
    *幻读:一个事物操作(DML)数据表中所有记录,另一个事物添加了一条数据,则第一个事物查询不到自己的修改
    隔离级别:
    *read uncommitted:读未提交
    产生问题:脏读、不可重复读、幻读
    *read committed:读已提交(Oracle默认)
    产生问题:不可重复读、幻读
    *repeatable read:可重复度(MySQL默认)
    产生问题:幻读
    *serializable:串行话
    注意:隔离级别从小到大安全性越来越高,但是效率越来越低
    数据库查询隔离界别:select @@tx_isolatioon
    数据库设置隔离界别:set global transaction isolation level 级别字符串
    4. 一致性:事物操作前后,数据总量不变

9.JDBC

  • 概念:Java Database Connectivity Java 数据库连接,Java语言操作数据库
  • 本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口编程,真正执行的代码是驱动jar包中的实现类。
  • 步骤
    1. 导入驱动jar包 mysql-connector-java-8.0.11.jar:复制到项目的libs目录下,add as library
    2. 注册驱动
    3. 获取数据库的连接对象Connection
    4. 定义sql语句
    5. 获取执行sql语句的对象 Statement
    6. 执行sql,接收返回结果
    7. 处理结果
    8. 释放资源
public static void main(String[] args) throws Exception {
        //1. 导入驱动jar包
        //2. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //3. 获取数据库的连接对象Connection
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/foods?useSSL=false", "root", "123456");
        //4. 定义sql语句
        String sql="update users set name='hello' where id='13525822910'";
        //5. 获取执行sql语句的对象 Statement
        Statement stmt = conn.createStatement();
        //6. 执行sql,接收返回结果
        int cot = stmt.executeUpdate(sql);
        System.out.println(cot);
        stmt.close();
        conn.close();
    }

遇到的异常:The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone 即MySQL与电脑时区不同
解决办法:set global time_zone='+8:00'或者jdbc:mysql://localhost:3306/foods?serverTimezone=GMT%2B8'
遇到的警告:WARN: Establishing SSL connection without server's identity verification is not recommended. According to My... 原因是MySQL在高版本需要指明是否进行SSL(保障Internet数据传输安全利用数据加密)
解决办法:jdbc:mysql://localhost:3306/foods?useSSL=false'加了?useSSL=false

详解各个对象

DriverManager:驱动管理对象

功能:

  1. 注册驱动:告诉程序该使用哪个数据驱动jar
    static void registerDriver(Driver driver)注册给定的驱动程序DriverManager
    写代码使用:Class.forName("com.mysql.jdbc.Driver");
    通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static{
				try{
					java.sql.DriverManager.registerDriver(new Driver());
				}catch(SQLException e){
					throw new RuntimeException("Can't register driver!");
				}
		}

注意:MySQL5之后可以省略注册驱动的步骤

  1. 获取数据库连接static Connection getConnection(String url, String user,String password)
    url:指定连接的路径:jdbc:mysql://ip地址(域名):端口号/数据库名称
    例子🍹:jdbc:mysql://localhost:3306/foods
    如果连接的是本机MySQL服务器,并且服务默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称
Connection:数据库连接对象

功能

  1. 获取执行sql的对象Statement createStatement() PreparedStatement prepareStatement(String sql)
  2. 管理事务:
    开启事务:void setAutoCommit(boolean autoCommit)调用该方法设置参数为false,即开启事务
    提交事物:void commit()
    回滚事务:void rollback()
Statement:执行sql的对象

方法

  1. boolean execute(String sql)可以执行任意 SQL 语句(了解)
  2. int executeUpdate(String sql)执行DML语句,DDL语句。返回值是影响的行数,可以判断DML语句执行是否成功,>0成功
  3. ResultSet executeQuery(String sql):执行DQL语句

举个例子🐷

public static void main(String[] args) {
        Statement stmt = null;
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String sql="insert into users values('13325822910','sss','154878989','css/images/Steven.jpg')";
            conn = DriverManager.getConnection("jdbc:mysql:///foods?useSSL=false", "root", "123456");
            stmt = conn.createStatement();
            int cot = stmt.executeUpdate(sql);
            System.out.println(cot);
            if(cot>0){
                System.out.println("yes!");
            }else {
                System.out.println("no!");
            }
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
ResultSet:结果集对象,封装查询结果

方法

  1. boolean next()将光标从当前位置向下移一行
  2. get×××():获取数据
    ×××:代表数据类型 如int getInt()
    参数:int:代表列的编号,从1开始getString(1);String:代表列的名称
public static void main(String[] args) {
        Statement stmt = null;
        Connection conn = null;
        ResultSet rs =null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String sql="select * from users";
            conn = DriverManager.getConnection("jdbc:mysql:///foods?useSSL=false", "root", "123456");
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            while (rs.next()){//遍历
                String name=rs.getString("name");
                System.out.println(name);
            }
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
PreparedStatement:执行sql的对象
  1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串拼接,会造成安全性问题
  2. 解决sql注入问题:使用PreparedStatement对象
  3. 预编译sql:参数使用?占位符
  4. 步骤
    1. 导入驱动jar包 mysql-connector-java-8.0.11.jar:复制到项目的libs目录下,add as library
    2. 注册驱动
    3. 获取数据库的连接对象Connection
    4. 定义sql语句 注意:sql的参数使用作为占位符 如select * from user where username=? and password=?
    5. 获取执行sql语句的对象 PreparedStatement 方法Connection.preparStatement(String sql)
    6. 给?赋值 方法set×××(参数1,参数2) 参数1:?的位置,从1开始 参数2:?的值
    7. 执行sql,接收返回结果,不需要传递sql语句了
    8. 处理结果
    9. 释放资源
  5. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作
    1. 可以防止sql注入问题
    2. 效率更高

JDBC工具类

  • 目的:简化书写
  • 分析
    1. 抽取注册驱动
    2. 抽取一个方法获取连接对象
    不想传参,还得保证工具类的通用性,通过配置文件来解决
    jdbc.properties
    url=
    user=
    password=
    3. 抽取一个方法释放资源
/**
 * JDBC工具类
 */
public class tool {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    /**
     *文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
     */
    static {
        //读取资源文件,获取值
        try {
            //Properties集合类
            Properties pro=new Properties();
            //动态获取src路径下的文件的方式--->ClassLoader类加载器
            ClassLoader classLoader = tool.class.getClassLoader();
            URL resource = classLoader.getResource("jdbc.properties");
            String path=resource.getPath();
            //加载文件
            pro.load(new FileReader(path));
            //获取数据,赋值
            url=pro.getProperty("url");
            user=pro.getProperty("user");
            password=pro.getProperty("password");
            driver=pro.getProperty("driver");
            //注册驱动
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     * @return 连接对象
     */
    public static Connection getConn() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }

    /**
     * 释放资源
     * @param stmt
     * @param conn
     */
    public static void close(Statement stmt, Connection conn){
        if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void close(ResultSet rs, Statement stmt, Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

JDBC控制事务

  1. 事务:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败
  2. 操作:
    1. 开启事务:start transaction
    2. 回滚:rollbake
    3. 提交:commit
  3. 使用connection对象来管理事务:
    开启事务:void setAutoCommit(boolean autoCommit)调用该方法设置参数为false,即开启事务,在执行sql之前开启事务
    提交事物:void commit()当所有sql都执行完提交事务
    回滚事务:void rollback()在catch中回滚事务

10.数据库连接池

  • 概念:其实就是一个容器(集合),存储数据库连接的容器
    当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完后,会将连接对象归还给容器
  • 好处
    1. 节约资源
    2. 用户访问高效
  • 实现
    1. 标准接口:DataSource javax.sql包下的
    方法:
    获取连接getConnection()
    归还连接:如果连接的对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了,而是归还连接
    2. 一般我们不去实现它,有数据库厂商来实现
    *C3P0:数据库连接池技术
    *Druid:数据库连接池实现技术,由阿里巴巴提供的

C3P0:数据库连接池技术

  • 步骤
    1. 导入jar包 c3p0-0.9.5.5.jar,mchange-commons-java-0.2.19.jar
    2. 定义配置文件
    *名称:c3p0-config.xml
    *路径:直接将文件放在src目录下即可
    *创建核心对象 数据库连接池对象ComboPooledDataSource
    *获取连接:getConnection
public static void main(String[] args) throws SQLException {
        //创建数据库连接池 不指定使用默认配置 ComboPooledDataSource(“指定名称”)
        DataSource ds=new ComboPooledDataSource();
        //获取连接对象
        for (int i = 0; i < 11; i++) {
            Connection conn=ds.getConnection();
            //打印
            System.out.println(conn);
            if(i==5)
                conn.close();//归还到连接池
        }
    }

Druid:数据库连接池实现技术,由阿里巴巴提供的(优秀)

  • 步骤
    1. 导入jar包 druid-1.0.9.jar
    2. 定义配置文件:druid.properties 可以叫任意名称,放置在任意目录下
    3. 加载配置文件
    4. 获取数据库连接池对象:通过工厂类来获取 DruidDataSourceFactory
    5. 获取连接:getConnection
public static void main(String[] args) throws Exception {
        Properties pro=new Properties();
        InputStream in = druiddemo.class.getClassLoader().getResourceAsStream("druid.properties");
        pro.load(in);
        DataSource dataSource = DruidDataSourceFactory.createDataSource(pro);
        Connection conn = dataSource.getConnection();
        System.out.println(conn);
    }

严重: maxIdle is deprecated 表示maxIdle已过时,在配置文件中删除它
严重: testWhileIdle is true, validationQuery not set
校验没有设置,在配置文件中加
validationQuery:SELECT 1
testWhileIdle:true
testOnBorrow:false
testOnReturn:false

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///foods?useSSL=false
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
minIdle=3
validationQuery:SELECT 1
testWhileIdle:true
testOnBorrow:false
testOnReturn:false
  • 定义工具类
    1. 定义一个类JDBCUtils
    2. 提供静态代码块加载配置文件,初始化连接池对象
    3. 提供方法
    获取连接方法:通过数据库连接池获取连接
    释放资源
    获取连接池的方法
package datasource.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * Druid连接池的工具类
 */
public class JDBCUtils {
    //1.定义一个成员变量
    private static DataSource ds;
    static {
        //加载配置文件
        try {
            Properties pro=new Properties();
            pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //2.获取DataSource
            ds= DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取连接
     */
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    /**
     * 释放资源
     */
    public static void close(Statement stmt,Connection conn){
        /*if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();//归还连接
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }*/
        close(null,stmt,conn);
    }
    public static void close(ResultSet rs, Statement stmt, Connection conn){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 获取连接池的方法
     */
    public static DataSource getDataSource(){
        return ds;
    }
}

测试

public static void main(String[] args) {
        /**
         * 完成添加操作,给users添加一条记录
         */
        Connection conn=null;
        PreparedStatement pstmt=null;
        //1.获取连接
        try {
            conn= JDBCUtils.getConnection();
            //2.定义sql
            String sql="insert into users values(?,?,?,'css/images/Steven.jpg')";
            //3.获取pstmt对象
            pstmt=conn.prepareStatement(sql);
            //4.赋值
            pstmt.setString(1,"15745825695");
            pstmt.setString(2,"kl");
            pstmt.setString(3,"1c5745825695");
            //5.执行
            int count=pstmt.executeUpdate();
            System.out.println(count);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //6.释放资源
            JDBCUtils.close(pstmt,conn);
        }
    }

11.Spring JDBC

Spring框架提供的对JDBC简单封装。提供了JDBCTemplate对象简化JDBC的开发

  • 步骤
    1. 导入jar包
    2. 创建jdbcTemplate对象。依赖于数据源DataSource JdbcTemplate template=new JdbcTemplate(ds);
    3. 调用jdbcTemplate的方法来完成CRUD操作
    update() 执行DML语句,增删改语句
    queryForMap()查询结果将结果封装为Map集合,将列名作为key,值作为value,这个方法查询的结果集长度只能是1
    queryForList()查询结果将结果封装为List集合,将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
    query()查询结果将结果封装为JavaBean对象
    query的参数:RowMapper,一般我们使用BeanPropertyRowMapper实现类。
    可以完成数据到Javabean的自动封装new BeanPropertyRowMapper<类型>(类型.class)
    queryForObject()查询结果将结果封装为对象,一般用于聚合函数的查询

例子🥠

public static void main(String[] args) {
        JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
        String sql="update users set name='hy' where id=?";
        int update = template.update(sql, "13525822910");
        System.out.println(update);
    }
public class demo2 {
    //Junit单元测试
    JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
    @Test
    public void test1(){
        String sql="select * from users";
        List<Users> list=template.query(sql,new BeanPropertyRowMapper<Users>(Users.class));
        for (Users users : list) {
            System.out.println(users);
        }
    }
    @Test
    public void test2(){
        String sql="select count(id) from users";
        Long l=template.queryForObject(sql,long.class);//一般用来执行聚合函数
            System.out.println(l);
    }
}

jar在这里

在这里👇👇👇数据库驱动jar包,C3P0的jar包和Druid的jar包,JDBCTemplate的jar包
链接:点我   提取码: xpfa

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值