文章目录
Java基础知识(三)
1.Junit单元测试
- 单元测试:
1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值
2.白盒测试:需要写代码的。关注程序具体的执行流程
Junit使用:白盒测试
步骤:
- 定义一个测试类(测试用例)
建议:
测试类名:被测试的类名Test
包名:×××.×××.××.test - 定义测试方法:可以独立运行
建议:
方法名:test测试的方法名
返回值:void
参数列表:空参 - 给方法加@Test
- 导入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.数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}省略
- 要求:
- 元注解:描述注解的注解
- @Target:描述注解能够作用的位置
ElemetType取值:TYPE
:可以作用于子类上;METHOD
:可以作用于方法上;FIELD
:可以作用于成员变量上 - @Retention:描述注解被保留的一个阶段
@Retention(RetentionPolicy.RUNTIME)
:当前被描述的注解会保留到clss字节码文件中,并被JVM读取到 - @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否被子类继承
- 格式:
@Target(value={ElementType.TYPE})//表示MyAnno3注解只能作用于类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno3 {
}
- 在程序中使用(解析)注解:获取注解中定义的属性值
- 获取注解定义的位置的对象(Class,Method,Field…)
- 获取指定的注解
getAnnotation(Class)
- 调用注解中的抽象方法获取配置的属性值
/**
* 描述需要去执行的类名和方法名,和方法名
*/
@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基本概念
- 概念:用于存储和管理数据的仓库
- 特点:
- 持久化存储数据。其实数据库就是一个文件系统
- 方便存储和管理数据
- 使用了统一的方式操作数据库 -----SQL
- 常见的数据库软件
4.2 MySQL数据库软件
管理员权限打开cmdnet stop mysql
net start mysql
- MySQL登录
- mysql -uroot -p密码
- mysql -hip -uroot -p连接目标的密码
- mysql --host=ip --user=root --password=密码
- MySQL退出
- exit
- quit
- MySQL目录结构
- MySQL安装目录
- MySQL数据目录
5.SQL
- 什么是SQL
Structured Query Language:结构化查询语言。其实就是定义了操作所有关系型数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。 - SQL通用语法
- SQL语句可以单行或多行书写,以分号结尾。
- 可使用空格和缩进来增强语句的可读性。
- MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。
- 三种注释:单行注释:
-- 注释内容
或#注释内容
(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数据库,判断是否存在,并指定字符集是gbkcreate 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. 起别名:as
as也可以省略 -
条件查询
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.数据库的备份和还原
- 命令行
备份语法:mysqldump -u用户名 -p密码 > 保存的路径
还原:登录数据库;创建数据库;使用数据库;执行文件:source 文件路径
- 图形化工具
8.事物
- 概念:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败
- 操作:
1. 开启事务:start transaction
2. 回滚:rollbake
3. 提交:commit
4. 事物提交的两种方式
自动提交:MySQL数据库中事物默认自动提交;一条DML语句会自动提交一次事物
手动提交:需要先开启事物再提交;Oracle数据库默认是手动提交事物
修改事物的默认提交方式:
查看事物的默认提交方式select @@autocommit
1代表自动提交,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:驱动管理对象
功能:
- 注册驱动:告诉程序该使用哪个数据驱动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之后可以省略注册驱动的步骤
- 获取数据库连接
static Connection getConnection(String url, String user,String password)
url:指定连接的路径:jdbc:mysql://ip地址(域名):端口号/数据库名称
例子🍹:jdbc:mysql://localhost:3306/foods
如果连接的是本机MySQL服务器,并且服务默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称
Connection:数据库连接对象
功能
- 获取执行sql的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
- 管理事务:
开启事务:void setAutoCommit(boolean autoCommit)
调用该方法设置参数为false,即开启事务
提交事物:void commit()
回滚事务:void rollback()
Statement:执行sql的对象
方法
boolean execute(String sql)
可以执行任意 SQL 语句(了解)int executeUpdate(String sql)
执行DML语句,DDL语句。返回值是影响的行数,可以判断DML语句执行是否成功,>0成功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:结果集对象,封装查询结果
方法
boolean next()
将光标从当前位置向下移一行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的对象
- SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串拼接,会造成安全性问题
- 解决sql注入问题:使用PreparedStatement对象
- 预编译sql:参数使用?占位符
- 步骤
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. 释放资源 - 注意:后期都会使用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. 开启事务:start transaction
2. 回滚:rollbake
3. 提交:commit
- 使用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对象。依赖于数据源DataSourceJdbcTemplate 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