Java基础知识总结(六)

网络编程:

​ 数据在网络中不断进行传输

网络三要素:

ip地址–端口–协议

  找到高圆圆
    1)网络中需要知道ip地址
    2)找到她的ip,对她说话---> 对着"耳朵"-----> 端口port
    3)对她说话 "i love you"--->   她听不懂,"我爱你"---->需要有一个规则 ----"协议"

      ip 使用点分十进制法,中间使用.隔开


            A类  国家大部门---->前一个号段是网络号段,后面三个主机号段
            B类  校园/大公司服务器机房/:前面两个网络号段,后面使用两个注解号段
            C类  私人地址:前面三个为网络号段,后面是主机号段
    
                192.168.251.1--->
      port端口
            port端口 360软件都可以查看你电脑所有客户端软件的端口号
                范围:0~65535  里面0~1024属于保留端口
      传输协议
        UDP协议--->数据报包(数据包的报文)
            1)不需要建立连接通道
            2)不可靠协议,不安全的
            3)发送数据大小有限制
        TCP协议--->最基本的字节流的方式发送数据
            1)就必须连接通道
            2)可靠协议,一种安全
            3)发送数据大小无限制



      需要访问百度服务器
      http://www.baid.com:80
      --->http:// 协议--->底层基于tcp(建立通道)
    
        www.baidu.com---->ip地址
        端口:默认80可以不写(省略)

​    查看ip地址 ipconfig windows系统
​            在Linux系统---->centos-->ifconfig

获取计算机的主机名称或者获取计算机的ip地址字符串形式?

public static InetAddress getByName(String host)

throws UnknownHostException

通过计算机主机名称或者是字符串形式的ip地址--->获取互联网ip地址对象
 *
 */
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {

```
//public static InetAddress getByName(String host)throws UnknownHostException
//1)可以通过计算机机器名称获取ip地址对象
InetAddress inetAddress = InetAddress.getByName("DESKTOP-Q62EUJH"); //还可以指定ip地址字符串
System.out.println(inetAddress);//DESKTOP-Q62EUJH/192.168.1.5

//互联网ip地址对象:  主机名称/ip地址字符串
//public String getHostName() :通过ip地址对象获取计算机机器名
String hostName = inetAddress.getHostName() ;
System.out.println(hostName);
//获取里面的包含ip地址字符串形式
```

//        public String getHostAddress()
    String ip = inetAddress.getHostAddress();
    System.out.println(ip);
}
}

UDP接收端的代码实现

1)创建接收端的Socket对象,绑定端口

2)创建一个接收容器—>数据包—>自定义字节缓冲区,将发送的数据包

3)接收

4)从接收容器中解析数据包的实际内容数据

5)展示数据

先运行接收端,而且接收端不能运行多次,否则端口被占用!
*/
public class UDPReceiver {
public static void main(String[] args) throws IOException {
    //1)创建接收端的Socket对象,绑定端口
    //public DatagramSocket(int port)  throws SocketException
    DatagramSocket ds = new DatagramSocket(10086) ;

```
//2)创建一个接收容器--->数据包--->自定义字节缓冲区,将发送的数据包
```

   // public DatagramPacket(byte[] buf, int length)构造一个DatagramPacket用于接收长度的数据包length 。
    byte[] bytes = new byte[1024] ;//1024或者1024整数倍
    int length = bytes.length ;
    DatagramPacket dp = new DatagramPacket(bytes,length) ; //将发送端数据缓冲到这个接收容器中

```
//3)接收,以上面这个接收容器来接收
//public void receive(DatagramPacket p)throws IOException
ds.receive(dp);

//4)从接收容器中解析数据包的实际内容数据
//从接收容器中获取public byte[] getData() 实际缓冲区的对象(从上bytes分段取数据)
byte[] bytes2 = dp.getData();
//获取里面实际缓冲区的长度
```

   // public int getLength()
    int length2 = dp.getLength();
    //展示数据---分段取数据,每次从0开始取实际长度
    String msg = new String(bytes2,0,length2) ;
    //数据包里面获取哪一个ip地址发来的--->ip地址字符串形式
    //public InetAddress getAddress()--->InetAddress--->getHostAddress()--->String IP地址字符串
    String ip = dp.getAddress().getHostAddress() ;
    System.out.println("data from --->"+ip+",发送内容是:"+msg);

```
//释放资源
ds.close();
```


    }

}

Upd方式—>不需要建立连接通道,不可靠连接

Udp发送端的步骤

1)创建发送端的socket

2)创建数据报包

3)使用发送端的Socket将数据存储数据包中, 发送(本质存储数据包)

4)释放资源

public class UdpSend {
public static void main(String[] args) throws IOException {
    //1)创建发送端的socket
    //此类表示用于发送和接收数据报数据包的套接字。 DatagramSocket
    //public DatagramSocket() throws SocketException
    DatagramSocket ds = new DatagramSocket() ;

    // 2)创建数据报包DatagramPacket
    /**
     * public DatagramPacket(byte[] buf,  要发送数据---转换成字节数组
     *                       int length,  实际字节数长度
     *                       InetAddress address,  ip地址对象
     *                       int port)  端口号
     */
    byte[] bytes = "hello,UDP我来了".getBytes() ;
    int length = bytes.length ;
    InetAddress inetAddress = InetAddress.getByName("192.168.1.5");//如果自己玩,没有网线,无线ip不断变的--->本地回环地址127.0.0.1
    int port = 10086 ;
    DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port);
    
    //3)使用发送端的Socket将数据存储数据包中, 发送(本质存储数据包)
    //public void send(DatagramPacket p) throws IOException
    ds.send(dp) ;
    
    //4)释放资源
    ds.close() ;

}
}

UDP发送端可以不断发送数据,接收点不断展示数据,发送端可以自定义结束条件

发送端不断发送数据
需求:

UDP发送端可以不断键盘录入数据,接收端不断展示数据,

发送端可以自定义结束条件 (课堂练习)
*/
public class UdpSend {

public static void main(String[] args) {
    //UDP发送端可以不断键盘录入数据

//创建发送端的socket
DatagramSocket ds = null ;
try {
   ds = new DatagramSocket() ;
    //键盘录入可以使用BufferedReader--->读一行
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
    String line = null ;
    //一次读一行内容
    System.out.println("请您输一个数据");
    while((line=br.readLine())!=null){
        if("over".equals(line)){
            break;
        }
        //创建数据报包,将数据存储在数据包中
        DatagramPacket dp = new DatagramPacket(line.getBytes()
        ,line.getBytes().length,
        InetAddress.getByName("10.35.162.121"),

   6666) ;
                 //发送数据报包
                 ds.send(dp) ;
             }
         } catch (IOException e) {
             e.printStackTrace();
         }finally {
             if(ds!=null){
                 ds.close() ;
             }
         }

}

}
接收端不断接收数据并展示数据
接收端不断展示数据------->接收端模拟真实,不需要关闭
*/
public class UdpReceive {
public static void main(String[] args) {

try {
    //创建接收端的Socket对象
    DatagramSocket ds = new DatagramSocket(6666) ;while(true){//创建一个接收容器//自定义一个字节数组缓冲区byte[] buffer = new byte[1024] ;int bufferLength =  buffer.length ;DatagramPacket dp = new DatagramPacket(buffer,bufferLength) ;//接收数据容器
​        ds.receive(dp) ;//解析接收容器中真实内容byte[] bytes = dp.getData();int length = dp.getLength();//每次0开始读取字节数---转成StringString receiveMsg = new String(bytes,0,length) ;//获取ip地址字符串String ip = dp.getAddress().getHostAddress() ;//展示数据System.out.println("data from-->"+ip+",conent is :"+receiveMsg);}
} catch (IOException e) {
​    e.printStackTrace();
}

}
}

TCP客户端创建

1)创建客户端的Socket对象,指定ip和端口

2)获取客户端通道内容字节输出流对象,写数据

3)释放资源

public class TcpClientDemo {
public static void main(String[] args) throws IOException {
    //1)创建客户端的Socket对象,指定ip和端口
    //Socket(String host, int port)
    Socket socket = new Socket("10.35.162.121",1888) ;
    //2)获取客户端通道内容字节输出流对象,写数据
    //public OutputStream getOutputStream()
    OutputStream outputStream = socket.getOutputStream();
    outputStream.write("hello,TCP我来了".getBytes());//客户端获取通道字节输入流对象,读服务器端的反馈的数据InputStream in = socket.getInputStream();byte[] bytes = new byte[1024] ;int length = in.read(bytes);String fkMsg = new String(bytes,0,length) ;System.out.println(fkMsg);//3)释放资源
​    outputStream.close();
}

}

TCP服务端创建

1)创建服务器端的Socket对象

2)监听客户端的连接

3)获取监听到的客户端的通道内的自节输入流对象,读数据

4)释放服务器端的资源

public class TcpServerDemo {
public static void main(String[] args) throws IOException {
    //1)创建服务器端的Socket对象
    //public ServerSocket(int port)
    ServerSocket ss = new ServerSocket(1888) ;
    System.out.println("服务器正在等待客户端连接请求...");
    //2)监听客户端的连接
   // public Socket accept()throws IOException
    Socket socket = ss.accept();//阻塞式方法,没有客户端连接,一直等待
    System.out.println("客户端已连接");

//3)获取监听到的客户端的通道内的字节输入流对象,读数据
//public InputStream getInputStream()
InputStream inputStream = socket.getInputStream();
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int length = inputStream.read(bytes);
//转换成String
String receiveMsg =  new String(bytes,0,length) ;

//获取ip地址对象,同时ip地址字符串形式

   // public InetAddress getInetAddress()
    String ip = socket.getInetAddress().getHostAddress();
    System.out.println("data from --->"+ip+",content is-->"+receiveMsg);

//服务器端反馈给客户端数据
//服务器端可以获取监听客户端通道内字节输出流,写数据
OutputStream out = socket.getOutputStream();
out.write("我这边已经收到数据!".getBytes());

//        * 4)释放服务器端的资源
        ss.close();

}

}

TCP客户端不断创建数据,服务端不断接受数据

TCP客户端不断键盘录入数据

TCP客户端不断键盘录入数据
public class TcpClient {
public static void main(String[] args) throws IOException {
    //1)创建客户端Socket
    Socket socket  = new Socket("10.35.162.121",8888) ;

//2)可以创建BufferedReader 一次读取一行,录入数据
BufferedReader br=  new BufferedReader(new InputStreamReader(System.in)) ;
//获取通道字节输出流对象,写数据--->字节输出流---->包装字符流
OutputStream out = socket.getOutputStream();
//可以使用字符流把上面的通道内的字节数出流包装
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out)) ;
String line = null ;
while((line=br.readLine())!=null){
    /*if("886".equals(line)){
        break;
    }*/
    //录入一行,给封装到通过的字符流写一行进去
    bw.write(line) ;
    bw.newLine();
    bw.flush();
}

socket.close();

}
}

服务器端不断展示数据

 public class TcpServer {
public static void main(String[] args) throws IOException {

    //创建服务器端的Socket
    ServerSocket ss = new ServerSocket(8888) ;
    System.out.println("服务器正在等待连接");

   // int i= 0 ;
    while (true){//监听多个客户端了解
     //   i++;
        //监听客户端连接
        Socket socket = ss.accept();
      //  System.out.println("第"+i+"个客户端已连接");
        //获取监听客户端所在的通道内的字节输入流
        InputStream in = socket.getInputStream();
        //将桶内的字节输入流封装成字符流读
        BufferedReader br = new BufferedReader(new InputStreamReader(in)) ;
        //一次读取一行
        String line = null ;
        while((line=br.readLine())!=null){
          if("886".equals(line)){
              break;
          }
            System.out.println(line) ;//展示数据
        }
    }

    //模拟真实场景,服务器端不关

}
}

使用TCP将文本文件进行复制

分析:TCP创建字符缓冲输入流—>读UdpReceive.java文件 一次读一行

TCP客户端获取通道字节输入流—>封装字符缓冲输出流—>写一行—发给服务器端

public class TcpClientTest {
public static void main(String[] args) throws IOException {
    //创建客户端的Socket对象
    Socket s = new Socket("10.35.162.121",2222) ;
    //创建字符缓冲输入流对象
    BufferedReader br = new BufferedReader(
            new FileReader("UdpReceive.java")) ;

//获取客户端通道的字节输出流---->包装成BufferedWriter:字符缓冲输出流
BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(s.getOutputStream())) ;

//每次从.java文件读取一行,给通道流的流中写一行
String line = null ;
while((line=br.readLine())!=null){
    bw.write(line) ;
    bw.newLine();
    bw.flush();
}
System.out.println("文件读完,发送过去了...");
//释放资源
br.close();
s.close();

}
}

TCP服务器端将复制客户端的文件内容,复制到D://2211//day27_code_resource//Copy.java里面

分析:

TCP获取监听客户端的字节输入流—>封装字符缓冲输入流—>一读取一行

创建字符缓冲输出流—>写一行D://2211//day27_code_resource//copy.java到这个文件中

public class TcpServerTest {
public static void main(String[] args) throws IOException {
    //创建服务器端的Socket对象
    ServerSocket ss = new ServerSocket(2222) ;
    //监听客户端连接
    Socket s = ss.accept();
    //获取监听客户端所在的通道内字节输入流对象---->包装成字符缓冲输入流
    BufferedReader br = new BufferedReader(
            new InputStreamReader(s.getInputStream())) ;

//将监听客户端的通道内的字节流(已经被包装了字符缓冲输入流)的内容----通过的字符缓冲输出流写入到文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\EE_2211\\day27_code_resource\\copy.java")) ;
//一次读取一行,写一行到文件中
String line = null ;
while((line=br.readLine())!=null){
    bw.write(line);
    bw.newLine();
    bw.flush();
}

System.out.println("复制完毕");
//释放资源
bw.close();
ss.close();

}

}

TCP客户端的文本文件(当前项目下的UdpReceive.java文件),

TCP服务器端复制文本文件到:D:\EE_2211\day27_code_resource\Copy.java文件中

TCP服务器端需要响应给客户端数据 “文件复制完毕”

TCP客户端展示服务器反馈的这个数据!

发现问题:

两个端都出现了互相等待了

因为客户端的文本文件读完,是null作为结束条件,但是服务器端不断的从通道内的输入流去读数据,

两端的通道不知道文件是否完了(服务器端不知道客户端的文件完没完),等待着写数据过来,

解决方案:

通知服务器端,别等了,文件读完了

1)自定义结束条件 ,服务器端读到自定义结束条件,就反馈!

弊端:如果文件第一句话恰好是自定义的结束条件,就不好

2)推荐:在客户端这边有个终止通道内的流 没有数据写过去了!禁止输出流输出!

public void shutdownOutput() throws IOException

public class Tcp_ClientTest2 {
public static void main(String[] args) throws IOException {
    //创建客户端的Socket对象
    Socket s = new Socket("10.35.162.121",2222) ;
    //创建字符缓冲输入流对象
    BufferedReader br = new BufferedReader(
            new FileReader("UdpReceive.java")) ;

//获取客户端通道的字节输出流---->包装成BufferedWriter:字符缓冲输出流
BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(s.getOutputStream())) ;

//每次从.java文件读取一行,给通道流的流中写一行
String line = null ;
while((line=br.readLine())!=null){//阻塞式方法.一直等待为null文件完毕
    bw.write(line) ;
    bw.newLine();
    bw.flush();
}

//写一个数据
//通道内的流写一句

   /* bw.write("over");
    bw.newLine();
    bw.flush();*///方案2: public void shutdownOutput()   throws IOException
​    s.shutdownOutput();//客户端要读取服务器反馈数据//获取通道内的字节输入流InputStream inputStream = s.getInputStream();//读取一个字节数组byte[] bytes = new byte[1024] ;int len = inputStream.read(bytes);System.out.println("客户端接收到了反馈数据:"+new String(bytes,0,len));//释放资源
​    br.close();
​    s.close();
}

}

反射

什么是反射?

编译某个类的时候---->获取这个类的字节码文件,然后去加载,调用里面的成员方法,访问成员变量,

通过构造方法创建对象!

编译过程:

对这个类的属性/成员/构造其进行校验---->类加载器(负责类的加载过程)

BoostrapClassLoader:启动类加载器,负责java核心库,rt.jar

如何获取一个类的字节码文件?

java.lang.Class:代表正在运行的java类或者接口,通过静态方法

public static Class forName(String classname):

参数:代表就是当前类或者接口的全限定名称 (包名.类名)

String---->全限定名称 java.lang.String

Person---->自定义的---->全限定名称:com.qf.reflect_06.Person

public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
    //如何获取一个类的字节码文件?

//第一种
//任意Java对象的getClass方法
Person p = new Person() ;
Class clazz = p.getClass();
System.out.println(clazz); //class com.qf.reflect_06.Person
//第二种
//任意Java类型的class属性
Class clazz2 = Person.class ;
System.out.println(clazz2);//第三种 推荐
   // java.lang.Class:代表正在运行的java类或者接口,通过静态方法
   // public static Class forName(String classname)Class clazz3 = Class.forName("com.qf.reflect_06.Person");System.out.println(clazz3) ;System.out.println(clazz==clazz3);System.out.println(clazz2==clazz3);System.out.println("----------------------------");Person p1 = new Person() ;Person p2 = new Person() ;System.out.println(p1==p2);
}

}

通过反射方式获取类的字节码文件之后,创建当前类对象!

之前的写法:考虑运行的代码 类名 对象名 = new 类名() ;

1)获取类的字节码文件对象

2)获取类的Constructor所在的构造器对象

3)通过它创建当前类实例

public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //之前的写法:通过无参构造方法创建对象
    //类名 对象名  = new 类名() ;
   // Person person = new Person() ;
   // System.out.println(person);
    System.out.println("-----------------------------------------------");

//通过反射获取构造器(Constructor)对象--->创建当前类实例
//1)获取类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_06.Person") ;
//2)获取类的Constructor所在的构造器对象

   // public Constructor<?>[] getConstructors()获取这个类中所有的公共的构造函数
   // Constructor[] constructors = clazz.getConstructors();
    //public Constructor<?>[] getDeclaredConstructors()获取这个类中所有的构造函数
   /* Constructor[] constructors = clazz.getDeclaredConstructors();
    for(Constructor con:constructors){
        System.out.println(con);
    }*/

//2)获取指定的构造方法所在的Constructor类对象
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取指定的公共的构造方法所在Constructor实例,参数--->参数类型的Class字节码文件对象
//参数是String----->String.class----结果 java.lang.String
Constructor con = clazz.getConstructor();// clazz.getConstructor(String.class)
//System.out.println(con);
//3)通过它创建当前类实例
//public T newInstance(Object... initargs):参数就给构造函数中参数进行实际赋值
Object obj = con.newInstance();
System.out.println(obj);

}
}

反射方式通过构造方法给成员赋值

之前的写法创建一个Person,给成员赋值

Person p = new Person("高圆圆",44) ;
*/
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

//Person p = new Person("高圆圆",44) ;  //之前的代码不行,私有构造方法不能new
//现在反射方式通过构造方法赋值
//1)获取当前类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_06.Person");
//2)获取构造器Constructor类对象--->带两个参数的
//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//参数里面是参数类型的字节码文件对象
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);

   // System.out.println(con);//private com.qf.reflect_06.Person(java.lang.String,int)

//AccessibleObject类提供Field(成员变量的类对象)/Method(成员方法类对象)/Constructor(构造器类对象)的基类
//提供功能:取消Java语言访问检查,暴力访问
//public void setAccessible(boolean flag) true:就是抑制Java语言访问检查功能
con.setAccessible(true);

//通过它创建当前类实例
Object obj = con.newInstance("高圆圆", 44);//IllegalAccessException:当前构造器私有不能访问
System.out.println(obj);

}
}

反射中如何调用成员方法

之前的写法:无参构造方法是公共的

Person p = new Person();

p.show() ;
 */
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
    //之前的写法
   // Person p = new Person();
    //p.show() ;
    System.out.println("----------------------------------");

//反射的写法
//1)获取类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_06.Person") ;//2)通过无参构造器创建器Constructor创建当前类实例//如果你这个类没有提供任何构造方法(系统提供无参)或者无参构造方法是公共的---->此时可以直接使用Class类的newInstacne()创建当前类实例//等价于下面这个写法Object obj = clazz.newInstance();System.out.println(obj);//System.out.println(obj);/*Constructor con = clazz.getConstructor();
​    Object obj = con.newInstance() ;*/
   // System.out.println(obj);// Method getMethods() ;//获取本类中所有的成员的method// Method getDeclaredMethods() ;//获取本类中所有的成员的method以及父类的Object//3)获取指定的成员方法所在的Method类对象
   // public Method getDeclaredMethod(String name, Class<?>... parameterTypes)获取指定的成员方的method类对象
   // public Method getMethod(String name, Class<?>... parameterTypes):获取指定的公共的成员方法的Method对象
   //第一个参数都是方法名//第二个参数是可变参数,方法的形式参数类型的Class(字节码文件对象)Method method = clazz.getMethod("show");//方法本身就是空参System.out.println(method);//调用方法,Method类---提供的invoke//public Object invoke(Object obj, Object... args)//第一个参数:当前类实例//第二个参数:给方法的形式参数赋的实际参数//调用方法到时候将指定的方法实际参数作用在当前类实例上
​    method.invoke(obj) ; //本身这个方法没有返回值,单独调用System.out.println("----------------------------------------------------------");/**

   * 反射调用这个
      private void method(String str){
        *         System.out.println(str);
                     *     }
                                */
                               Method m1 = clazz.getDeclaredMethod("method", String.class);
                              // System.out.println(m1);//private void com.qf.reflect_06.Person.method(java.lang.String)
             //私有方法取消Java语言检查
           m1.setAccessible(true) ;
           m1.invoke(obj,"hello,高圆圆") ;

}

}

单例设计模式

在内存中始终只有当前类的一个实例!(只有一个对象)
饿汉式和懒汉式
饿汉式:不会出现问题的单例设计模式

1)构造方法私有化
2)类一加载,成员位置—私有的静态实例变量(立即创建一个对象)
3)对外提供一个静态的公共的访问方法,返回值是当前类本身

// Runtime本身就是一个饿汉式
public Student{
    private static Student s = new Student() ;
    private Student(){}
    public  static Student getStudent(){
        return s ;
    }
    
}

懒汉式 :可能出现安全问题的单例设计模式 (懒加载/延迟加载)
一个用户有100账户,一对多的关系

1)构造方法私有化
2)在类的成员位置,声明一个静态类变量,当前类本身
3)提供对外的公共访问方法,返回值当前类本身,需要对当前变量进行判断,如果为null,在创建

public Student{
    private static Student s ;
    private Student(){}
    
    public  synchronized static Student getStudent(){
        if(s ==null){
            s = new Student() ;
        }
        return s ;
    }
    
}

数据库

什么是数据库?

数据库就是存储数据的一种仓库!

数据库存储数据特点:

1)存储空间非常大,可以存储百万条甚至上亿条数据,用户对数据库中的数据进行新增,查询,更新,删除等操作

2)数据独立性高

3)实现数据共享

4)减少数据的冗余度

5)通过数据里面的"事务"—>实现数据的一致性以及维护性!

常见的数据库有哪些

数据库分为两大类:

关系型数据库------>MySQL,SqlServer,Oracle,SQLite,MariaDB

​ SqlServer,Oracle:收费

​ Oracle—>中大型公司使用居多

​ Mysql—免费产品------中小型公司使用居多

非关系型数据库----->>Redis,Memcache,MongoDb,HBase

​ redis:后面讲(做数据缓存用的)

DDL语句的基础语法—库的操作

DDL语句:数据库定义语句 库和表的基本操作
库的操作:

1)查询所有库 show databases;

mysql> show databases ;

±-------------------+

| Database |
±-------------------+
| information_schema | mysql5.7对数据库元数据(创建时间,名称,访问权限…)的访问
| mysql | mysql的核心库,负责存储数据库的用户,权限,关键字等等
| performance_schema | mysql性能相关库—提供mysql性能相关的参数
| sys | 主要还是降低数据库各个之间的复杂度,让数据库运行更快
±-------------------+
11 rows in set (0.00 sec)

2)创建库的语法

2.1)create database if not exists 库名 ; 如果没有这个库,则创建该库
mysql> create database if not exists EE_2211;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;

±-------------------+

| Database |
±-------------------+
| information_schema |
| ee_2208_01 |
| ee_2208_02 |
| ee_2208_projects |

| ee_2211 | 创建的新库
| mybatis_01 |
| mybatis_02 |
| myee_2208 |
| mysql |
| performance_schema |
| racemall |
| sys |
±-------------------+

12 rows in set (0.00 sec)

2.2) 直接创建库 create database 库名;

mysql> create database javaee_2211_mysql;

Query OK, 1 row affected (0.00 sec)

mysql> show databases;

±-------------------+

| Database |
±-------------------+
| information_schema |
| ee_2208_01 |
| ee_2208_02 |
| ee_2208_projects |
| ee_2211 |
| javaee_2211_mysql | 创建辛苦
| mybatis_01 |
| mybatis_02 |
| myee_2208 |
| mysql |
| performance_schema |
| racemall |

| sys |
±-------------------+
13 rows in set (0.00 sec)

3)查询指定库的信息–包括它的字符集

​ show create database 库名;
mysql> show create database ee_2211;
±---------±-----------------------------------------------------------------------------------------------+
| Database | Create Database |
±---------±-----------------------------------------------------------------------------------------------+
| ee_2211 | CREATE DATABASE ee_2211 /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ |
±---------±-----------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

4)修改库的字符集

​ alter database 库名 default(省略) character set 字符集格式;
mysql> alter database ee_2211 character set gbk;
Query OK, 1 row affected (0.00 sec)

mysql> show create database ee_2211;
±---------±----------------------------------------------------------------+
| Database | Create Database |
±---------±----------------------------------------------------------------+
| ee_2211 | CREATE DATABASE ee_2211 /*!40100 DEFAULT CHARACTER SET gbk */ |
±---------±----------------------------------------------------------------+
1 row in set (0.00 sec)

5)删除库

​ 5.1)drop database if exists 库名; 如果存在库名,则删除
​ mysql> drop database if exists ee_2211;
Query OK, 0 rows affected (0.01 sec)

mysql> show databases;
没有这个库了
±-------------------+
| Database |
±-------------------+
| information_schema |
| ee_2208_01 |
| ee_2208_02 |
| ee_2208_projects |
| javaee_2211_mysql |
| mybatis_01 |
| mybatis_02 |
| myee_2208 |
| mysql |
| performance_schema |
| racemall |
| sys |
±-------------------+
12 rows in set (0.00 sec)

5.2)drop databse 库名 ; 直接删除库

mysql> drop database javaee_2211_mysql;
Query OK, 0 rows affected (0.00 sec)

mysql> show databases;
±-------------------+
| Database |
±-------------------+
| information_schema |
| ee_2208_01 |
| ee_2208_02 |
| ee_2208_projects |
| mybatis_01 |
| mybatis_02 |
| myee_2208 |
| mysql |
| performance_schema |
| racemall |
| sys |
±-------------------+
11 rows in set (0.00 sec)

DDL语句的基础语法–表的操作

– 普通注释 单行注释

特殊注释

/多行注释/

– 创建表的前提------------------>必须现在使用库----相当于进入到库(库就是文件夹)

– use 库名;
mysql> use myee_2211;
Database changed – 数据库改变了:进入到指定库中
mysql>

mysql常见的数据类型:

​ int,int(int类型的字符数)
​ 前者:int—>默认11位,当前这个字段(列的名称) 值本身的实际位数 (推荐)
​ 年龄这个字段-----> 25–>使用int就可以表示即可

​ 后者:int(3)---->给用户id(编号:1,2…n)---->不够三位补0 (不推荐后者)
​ id—>1---------------->001

​ varchar(字符长度):字符串类型
​ 姓名这个字段---->varchar(20) ---->最大长度取到20, “高圆圆” 实际三个字符
​ 在mysql中字符串写的时候---->可以使用""双引号,也可以使用’'单引号
​ date:日期类型----->仅仅表示日期
​ datetime:日期+时间
​ timestap:时间戳—>当前插入数据或则修改/删除数据的即时时间 :2022-11-30 11:30:00
​ double:小数类型
​ double(3,2): 小数是3位数,小数点后保留2位
​ clob:大字符类型,某个表中某个字段—> 使用clob来存储大文本!
​ blob:大字节类型:存储大图片文件—大字节类型

查询库中有哪些表

show tables ;

mysql> show tables;
Empty set (0.00 sec)

创建表的语法

create table 表名(
字段名称1 字段类型1,
字段名称2 字段类型2,
字段名称3 字段类型3,

字段名称n 字段类型n
) ;

mysql> create table student(
    -> id int,
    -> name varchar(10),
    -> age int,
    -> gender varchar(5),
    -> avg_socre double(3,1)
    -> );
Query OK, 0 rows affected (0.05 sec)

查询表的结构

desc 表名; – 看到表的字段有哪些

mysql> desc student;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  |     | NULL    |       |
| name      | varchar(10) | YES  |     | NULL    |       |
| age       | int(11)     | YES  |     | NULL    |       |
| gender    | varchar(5)  | YES  |     | NULL    |       |
| avg_socre | double(3,1) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)

修改表的字段类型

alter table 表名 modify 字段名称 修改后字段类型;

mysql> alter table student modify name varchar(8);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc student;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  |     | NULL    |       |
| name      | varchar(8)  | YES  |     | NULL    |       |
| age       | int(11)     | YES  |     | NULL    |       |
| gender    | varchar(5)  | YES  |     | NULL    |       |
| avg_socre | double(3,1) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

修改字段名称, (注意这个语法不要直接同时修改字段类型)

alter table 表名 change 以前的字段名称 现在的字段名称 以前的字段类型;

mysql> alter table student change gender sex varchar(5) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc student;

+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  |     | NULL    |       |
| name      | varchar(8)  | YES  |     | NULL    |       |
| age       | int(11)     | YES  |     | NULL    |       |
| sex       | varchar(5)  | YES  |     | NULL    |       |
| avg_socre | double(3,1) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

修改表—>添加一个新的字段

alter table 表名 add 字段名称 字段类型;

mysql> alter table student add address varchar(50) ;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc student ;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | YES  |     | NULL    |       |
| name      | varchar(8)  | YES  |     | NULL    |       |
| age       | int(11)     | YES  |     | NULL    |       |
| sex       | varchar(5)  | YES  |     | NULL    |       |
| avg_socre | double(3,1) | YES  |     | NULL    |       |
| address   | varchar(50) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)

修改表— 删除表中某个字段

alter table 表名 drop 字段名称 ;

mysql> alter table student drop avg_socre;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc student ;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int(11)     | YES  |     | NULL    |       |
| name    | varchar(8)  | YES  |     | NULL    |       |
| age     | int(11)     | YES  |     | NULL    |       |
| sex     | varchar(5)  | YES  |     | NULL    |       |
| address | varchar(50) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

修改表名

alter table 表名 rename to 新表名; 也可以用 rename table 以前表名 to 新表名;

mysql> alter table student rename to stu;
Query OK, 0 rows affected (0.02 sec)

mysql> show tables;
+---------------------+
| Tables_in_myee_2211 |
+---------------------+
| stu                 |
+---------------------+
1 row in set (0.00 sec)

查询表的字符集

show create table 表名;

修改表的字符集

alter table 表名 character set 字符集格式;

复制表—快速去创建有一个结构相同的表

create table 新表名 like 旧表名;

mysql> create table teacher like student;
Query OK, 0 rows affected (0.02 sec)

mysql> show tables;
+---------------------+
| Tables_in_myee_2211 |
+---------------------+
| student             |
| teacher             |
+---------------------+
2 rows in set (0.00 sec)

删除表

drop table if exists 表名;如果存在表删除

mysql> drop table if exists teacher;
Query OK, 0 rows affected (0.02 sec)

mysql> show tables;
+---------------------+
| Tables_in_myee_2211 |
+---------------------+
| student             |
+---------------------+
1 row in set (0.00 sec)

drop table 表名; 直接删除表名

mysql> drop table student ;
Query OK, 0 rows affected (0.02 sec)

mysql> show tables;
Empty set (0.00 sec)

MySQL的DQL语句---->数据库查询语句(带条件查询分类)

– 单行注释
/多行注释/

特殊注释用井号

– 创建库

CREATE DATABASE Myee_2211;
USE Myee_2211;

创建表

CREATE TABLE student(
	id INT , -- 学生的编号
	NAME VARCHAR(10), -- 姓名
	age INT , -- 年龄
	gender VARCHAR(3), -- 性1
	avg_socre DOUBLE(3,1), -- 分数
	adderss VARCHAR(50)
) ;

查询表的结果显示列名信息

DESC student ;

DML语句:数据库操作语句,操作表的记录—插入,修改,删除,查询…

插入数据

插入全表数据insert into 表名 values(值1,值2,值3,值3…值n);
INSERT INTO student VALUES(1,'高圆圆',44,"女",99.5,'西安市南窑国际') ;

插入全表数据也支持一次插入多条

insert into 表名 values(值1,值2,值3,值3…值n),(值1,值2,…值n),(…) ;
INSERT INTO student VALUES(1,'高圆圆',44,"女",99.5,'西安市南窑国际'),

(2,'韩永辉',25,'男',90.5,'西安市南窑国际92排'),
(3,'薛宇豪',22,'男',92.0,'咸阳市'),
(4,'文章',35,'男',78.5,'宝鸡市') ;

插入数据–插入部分字段,没有插入的字段值就是null(空值)也可以支持一次插入多条数据,部分字段

insert into 表名(字段名称1,字段名称2.,等部分字段)values(值1,值2,值3…),(值1,值2,值3…),(…);
NSERT INTO student(id,NAME,age,gender,avg_socre) VALUES
(10,'马伊琍',44,'女',89.0),
(5,'马保国',65,'男',87.9);
INSERT INTO student(id,NAME,age,gender,avg_socre) VALUES
(6,'马苏',36,'',77.5);

修改数据

带条件修改(一次修改一个或者多个)—>条件:一般都是非业务字段去修改

update 表名 set 字段名称= 值 where 字段名称= 值;

update 表名 set 字段名称1= 值1,字段名称2=值2,… where 字段名称= 值;

需求:将id为2的学生的姓名修改为"韩伟"
UPDATE student SET NAME = '韩伟' WHERE id = 2 ;

需求:将id为4的学生姓名改为"张三丰",年龄30,平均成绩改为 80.5

UPDATE 
  student 
SET
  NAME = '张三丰',
  age = 30,
  avg_socre = 80.5 
WHERE id = 4 ;
修改的时候如果不带where条件,属于批量修改(很少用)

修改姓名为高圆圆
UPDATE student SET NAME = ‘高圆圆’;

DML语句删除表的记录

带条件删除

delete from 表名 where 字段名称 = 值; (条件一般使用非业务字段删除)

delete from 表名 where 字段名称1 = 值1 and 字段名称2 = 值2 …多个条件删除

需求:删除id为的学生信息
DELETE FROM student WHERE id = 1 ;

需求: 删除姓名为高圆圆,年龄是男的学生信息
DELETE FROM student WHERE NAME = '高圆圆' AND gender = '男' ;

删除全表数据

delete from 表名; 是删除全表数据,表结构还在

DELETE FROM student ;

删除全表数据:truncate table 表名;

TRUNCATE TABLE student ;

面试题delete from 表名 和 truncate table 表名都是删除全表,有啥区别?

前者:仅仅是删除表的全部数据,表结构还在
如果表中的id有自增长约束存在,不会影响自增长约束的值,下一次插入数据之后
在上一次id的基础之上继续自增!

后者:不仅仅将表的全部数据删除,
而且它会删除这个表,会在创建一个创建空表,直接影响了这种(非业务字段id)自增长
主键的值,下一次插入数据的时候,从1开始继续自增!

基本的查询:查询全表

select * from 表名; – 代表全部字段 (实际开发中禁用)

SELECT

FROM
student ;

如果在dos窗口查询表的记录—dos乱码,临时更改字符集

模糊查询数据库每个端字符集

show variables like ‘%character%’ ;

set character_set_clinet/server/results=gbk ;在dos窗口就不乱码了

查询语句DQL语句—使用最频繁的

基本查询—查询全表字段并且给别名 给别名as (省略)
SELECT 
  id AS '编号',
  NAME AS '姓名',
  age AS '年龄',
  sex AS '性别',
  address AS '住址',
  math AS '数学成绩',
  english AS '英语成绩' 
FROM
  student2 ;
基本查询—查询时候指定部分字段

查询学生的编号,姓名,数学和英语成绩

SELECT 
	id '编号',
	NAME '姓名',
	math '数学成绩',
	english '英语成绩'
FROM
	student2;
查询指定字段如何去重呢?关键字distinct

查询住址字段

SELECT 
	DISTINCT address '住址'
FROM 
	student2;

基本带条件查询

where 比较运算符 <=,>=,<,>,!=(在mysql使用 <>),=赋值
				and  && /or  ||
				between 值1 and  值2

需求:查询年龄是20的学生的编号,姓名,年龄,住址信息

SELECT 
  id '编号',
  NAME '姓名',
  age '年龄',
  address '住址' 
FROM
  student2 
WHERE age = 20 ;

查询年龄在18到20岁之间学生的所有信息

SELECT 
      *
FROM 
	student2
WHERE 
	age >=18 && age<=20; -- 逻辑双与&& 并列关系
SELECT 
      *
FROM 
	student2
WHERE 
	age >=18 AND age<=20;	-- mysql提供的关键字 and 并列关系
	
SELECT 
      *
FROM 
	student2
WHERE 
	age BETWEEN 18 AND 20;

需求:查询学生的姓名以及学生的总成绩

如果两个数据int类型字段求和的时候,如果某个字段是null,结果是null
mysql的ifnull(字段名称,预期值) 函数

SELECT 

NAME '姓名',
	(math+english) '总成绩'
FROM
	student2;

SELECT 
	NAME '姓名',
	(math+IFNULL(english,0)) '总成绩'
FROM
	student2;	

需求:查询年龄是20或者年龄是18或者年龄是45的学生所有信息

where条件可以使用 or 或者java语言||,in(值1,值2…)

SELECT 

FROM
  student2 
WHERE age = 20 
  OR age = 18 
  OR age = 45 ;
 -- 另一种写法
SELECT 

FROM
  student2 
WHERE age = 20 || age = 18 || age = 45 ;
-- 另一种写法:in(值1,值2.....)
SELECT 

FROM
  student2 
WHERE age IN (20, 18, 45) ;
查询某个字段为null的信息---->is null
查询字段不为null---->is not null

需求:查询英语成是null的学生的所有信息

select 

*

from
  student2 
where english = null;
SELECT 

*

FROM
  student2 
WHERE 
  english IS NOT NULL ;
查询某个字段不等于某个值 —使用!=或者mysql的 <>

需求:查询年龄不是20岁的学生所有信息

SELECT 

*

FROM
  student2 
WHERE age != 20 ;

-- 另一种写法

SELECT 

*

FROM
  student2 
WHERE age <> 20 ;
模糊查询mysql中所有的带有character字符集

SHOW VARIABLES LIKE ‘%character%’ ;

DQL之模糊条件查询 — 关键字 like

语法 select 字段列表 from 表名 where 字段名称 like ‘%xx%’

%:代表任意多个字符 (开发中使用居多)
_:代表任意一个字符

‘字符%’ ‘%字符%’ 也可以

需求:查询学生中所有姓马的学生信息
SELECT
*
FROM 
student2
WHERE 
NAME LIKE '%马%'
查询学生的姓名是三个字符的人的信息
SELECT 

*

FROM
  student2 
WHERE NAME LIKE '___' ;
DQL之聚合查询 —>结果是单行单例的
COUNT(字段名称) ---->使用最多  统计表的记录
	count里面 字段名称:一般非业务字段 (id:一般主键字段)
	如果字段某个值是null,不会统计
	
max(列名称):求最大值
min(列名称):求最小值	
avg(列名称):求当前列的平均值
sum(列名称):求当前这列的总和
统计学生表studnet2的总记录数(总条数)
select 
  count(ifnull(english,0)) 'total' 
from
  student2 ;

 SELECT COUNT(id) '总记录数' FROM student2;
需求:查询数学平均分

SELECT AVG(math) FROM student2;

英语总分

SELECT SUM(IFNULL(english,0)) ‘英语总分’ FROM student2;

最大值/最小值

SELECT MAX(math) FROM student2;
SELECT MIN(math) FROM student2;

聚合函数—完成sql语句嵌套 (单表操作/多表都可以)
查询数学成绩大于数学平均分的学生信息!

– 1)查询出数学成绩的平均分
– select avg(math) from student2; – 79.5000
– 2)查询数学成绩大于79.5000的学生信息

select 

from
  student2 
where math > 79.5000 ;
*/
-- 一步走
SELECT 

FROM
  student2 
WHERE 
math > 
  (SELECT 
AVG(math) 
  FROM
student2) ;

DQL语句之排序查询 order by

单独使用order by

select 字段列表 from 表名 order by 字段名称 排序规则;

排序规则:默认asc ,升序排序
desc 降序
如果有条件where,还有order by,where在前面满足条件,才排序!

针对某个字段排序

需求:数学成绩升序排序

SELECT 

FROM
  student2 
ORDER BY  math ASC ; -- asc可以省略不写
-- 需求:数学成绩大于70的学生按照降序排序
SELECT 

FROM
  student2 
WHERE math > 70 
ORDER BY math DESC ;
同时多个条件排序

需求:学生的数学成绩升序,英语成绩降序

SELECT 

FROM
  student2 
ORDER BY math ASC,-- 先按照数学排
  english DESC ;

DQL语句之分组查询 group by

注意:
1)分组查询里面可以select 查询分组字段
2)分组group by后面不能使用聚合函数

单独使用

select 字段列表 from 表名 group by 分组字段名称;

需求:按照性别分组,查询数学成绩的平均分 (显示的查出分组字段)
SELECT 
  sex '性别',
  AVG(math) '数学平均分' 
FROM
  student2 
GROUP BY sex ;

如果分组查询带where条件,

需求:按照性别分组,查询数学成绩平均分,

条件:学生数学成绩大于70的人参与分组

where条件和group by,where条件的在group by的前面
group by的后面不能使用where,先满足where条件,才参与分组!

select 
  sex '性别',
  avg(math) '数学平均分' 
from
  student2 
group by sex 
where math > 70 ;
*/
SELECT 
  sex '性别',
  AVG(math) '数学平均分' 
FROM
  student2 
WHERE math > 70 
GROUP BY sex ;

筛选having

注意
having的后面可以跟聚合函数,having在group by ,
where是gruop by的前面;

需求:按照性别分组,查询数学成绩平均分,

条件:学生数学成绩大于70的人参与分组,同时筛选出人数大于2的一组!

SELECT 
  sex '性别',
  AVG(math) '数学平均分',
  COUNT(id) total
FROM
  student2 
WHERE math > 70 
GROUP BY sex 
HAVING total  > 2 ;

分页查询limit

select 字段列表 from 表名 limit 起始行数,每页显示的条数;

起始行数= (当前页码数-1)*每页显示的条数;

查询第一页的数据

SELECT * FROM student2 LIMIT 0,2 ;

查询第二页的数据

SELECT * FROM student2 LIMIT 2,2 ;

select 字段列表 from 表名 limit 值;

SELECT * FROM student2 LIMIT 3 ;

如果复合查询:有where条件,还有limit,where在limit前面

SELECT * FROM student2 WHERE id > 2 LIMIT 4;

数据库约束(限定用户操作数据库的一种行为)

约束用户数据库的一种行为

默认约束 default

-- 创建一张表
CREATE TABLE test1(
	id INT, -- 编号
	NAME VARCHAR(10), -- 姓名
	age INT, -- 年龄
	gender VARCHAR(3)  DEFAULT '女' -- 姓名
 ) ;

 INSERT INTO test1 VALUES(1,'文章',35,'男'),(2,'高圆圆',30,'女') ;
 -- 插入部分字段
 INSERT INTO test1(id,NAME,age) VALUES(3,'张佳宁',29) ;
 -- 没有插入字段的值默认值null,为了防止这种数据出现,可以在创建表的时候
 -- 加入默认约束default,当没有插入这个字段,默认约束起作用!

DROP TABLE test1;
-- 通过sql将默认约束删除
-- 修改表的字段类型
ALTER TABLE test1 MODIFY gender VARCHAR(3)
INSERT INTO test1(id,NAME,age) VALUES(4,'刘亦菲',27) ;
-- sql添加,默认约束
ALTER TABLE test1 MODIFY gender VARCHAR(3) DEFAULT '女' ;

非空约束 not null

-- 当前这个值不能为null,不能直接添加数据给一个null
CREATE TABLE test1(
	id INT,
	NAME VARCHAR(10) NOT NULL -- 创建表的时候在指定字段后面加入not null
);
INSERT INTO test1 VALUES(1,'张三'),(2,'张三丰') ;
-- insert into test1 values(3,null) ; -- 某个字段值直接插入null值(非法行为)
-- Column 'name' cannot be null  当前这个值不能插入null值

-- 将默认约束删除了
-- alter table 表名 modify 字段名称 数据类型;
ALTER TABLE test1 MODIFY NAME VARCHAR(10) ;
-- 添加非空约束
ALTER TABLE test1 MODIFY NAME VARCHAR(10) NOT NULL ;
INSERT INTO test1 VALUES(3,NULL) ;


唯一约束 unqiue

限制id字段/有效身份信息(邮箱/手机号/身份证...)
CREATE TABLE test1(
	id INT ,
	NAME VARCHAR(10),
	telephone VARCHAR(11) UNIQUE -- 唯一约束:存在唯一索引inedex
) ;
INSERT INTO test1 VALUES(1,'高圆圆','13366668888'),(2,'王力宏','13322226666') ;
-- 添加手机号码重复 
-- INSERT INTO test1 VALUES(3,'文章','13366668888'); -- 手机号重复,防止这些字段重复(有效身份信息,可以在创建表的时候添加唯一约束)
-- -- Duplicate entry '13366668888' for key 'telephone' 这个字段有多个重复值出现

-- 删除唯一约束----(删除唯一索引的名称)
-- alter table 表名 drop index 索引名(索引名如果没有起名字默认是列的名称);
ALTER TABLE test1 DROP INDEX telephone ;

INSERT INTO test1 VALUES(3,'文章','13366668888');
-- 添加唯一约束
 -- 

 -- alter table 表名
  --  add constraint(声明)
  --  唯一约束的索引名称 
  -- unique(列名);
 ALTER TABLE test1 ADD CONSTRAINT index_telephone UNIQUE(telephone) ; 
-- uplicate entry '13366668888' for key 'index_telephone' 

DROP TABLE test1;

主键约束 primary key

 主键约束primary key ---特点非空且唯一
CREATE TABLE test1(
	id INT PRIMARY KEY , -- 主键约束
	NAME VARCHAR(10),
	gender VARCHAR(3)
);
INSERT INTO test1 VALUES(1,'张三','男'),(2,'李四','女') ;

INSERT INTO test1 VALUES(NULL,'高圆圆','女') ;
INSERT INTO test1 VALUES(1,'赵又廷','男') ;
-- 上面的sql直接插入null而且id重复,使用primary key (主键索引)


-- 通过sql删除主键
-- alter table 表名 drop primary key ;
ALTER TABLE test1 DROP PRIMARY KEY ;

-- 通过sql添加主键
-- alter table 表名 add primary key (列名称) ;
ALTER TABLE test1 ADD PRIMARY KEY(id) ;

自增长约束 auto_increment

一般自增长约束都是在主键字段上,保证唯一
-- 指定插入数据的值,下次在之前的值上继续自增1
DROP TABLE test1;
CREATE TABLE test1(
	id INT PRIMARY KEY  AUTO_INCREMENT, -- 主键约束 + 自增长
	NAME VARCHAR(10),
	gender VARCHAR(3)
);

INSERT INTO test1(NAME,gender) VALUES('张三','男'),('李四','女') ;
-- 指定插入id的值
INSERT INTO test1 VALUES(20,'高圆圆','女') ;
UPDATE test1 SET id = 25  WHERE id = 20 ; 
-- 自增长主键的值一定插入之后产生的而不是修改!
INSERT INTO test1(NAME,gender) VALUES('赵又廷','男') ;


-- mysql自带函数---查找数据库表中最后一次自增主键的值是多少
SELECT LAST_INSERT_ID();


SELECT * FROM test1 ;                               

外键约束 foreign key

 有一张表 员工表
CREATE TABLE employee(
	id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
	NAME VARCHAR(10) ,  -- 员工姓名
	gender VARCHAR(3) , -- 员工性别
	dept_name VARCHAR(10) -- 员工所在的部门名称
);
INSERT INTO employee(NAME,gender,dept_name)
VALUES('高圆圆','女','测试部'),
('张佳宁','女','运维部'),
('王宝强','男','开发部'),
('文章','男','开发部'),
('赵又廷','男','运维部') ;

/*
	问题:
		1)查询员工的所有信息---部门名称字段 冗余度大(重复度高)
		2)一张表描述了两个事情(员工信息,又有部门信息!)
		

​    解决方案:将这张表拆分两张表
​    一张表员工信息
​    一张表描述部门信息		

*/
DROP TABLE employee;

-- 创建一张部门表
CREATE TABLE dept(
	id INT PRIMARY KEY AUTO_INCREMENT, -- 部门编号
	NAME VARCHAR(10) -- 部门名称
);
INSERT INTO dept(NAME) VALUES('开发部'),('测试部'),('运维部');
-- 创建一个员工表
CREATE TABLE employee(
	id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
	NAME VARCHAR(10) , -- 员工姓名
	gender VARCHAR(3) , -- 性别
	dept_id INT -- 部门编号
);
INSERT INTO employee(NAME,gender,dept_id)
VALUES('高圆圆','女',2),
('张佳宁','女',3),
('王宝强','男',1),
('文章','男',1),
('赵又廷','男',3) ;

-- 插入了一个条数据:没有4号部门,但是依然能插入进去!
-- 防止出现问题,添加外键约束---让两个产生关联关系
INSERT INTO employee(NAME,gender,dept_id) VALUES('张三','男',4) ;


-- 创建员工表的时候,同时添加外键约束!
/*
外键作用的表---从表,另一张表:主表
	创建表的时候添加外键
	constraint (声明)
	 外键名--->起名字 主表名_从表名_fk
	 foriegn key (从表的字段名称)
	 references -- (关联)
	 主表名(主键字段的名称)
*/
CREATE TABLE employee(
	id INT PRIMARY KEY AUTO_INCREMENT, -- 编号
	NAME VARCHAR(10),  -- 姓名
	gender VARCHAR(3), -- 性别
	dept_id INT ,-- 部门编号
	CONSTRAINT -- 声明
	dept_emp_fk -- 外键名称
	FOREIGN KEY(dept_id) -- 作用在指定外键字段上
	REFERENCES -- 关联
	dept(id) -- 主表的主键字段
	
);



-- 有了外键之后,直接修改或者删除主表数据,前提需要让从表的数据跟主表没有关联
-- 这个时候才能修改和删除主表数据!
-- 1)将3号部门的员工删除了
DELETE FROM employee WHERE dept_id = 3 ;

-- 2)将3号部门在删除
DELETE FROM dept WHERE id = 3;

-- 有外键约束,修改--先修改从表,然后在修改主表

-- 将2号部门的员工---将部门编号设置1
UPDATE employee SET dept_id = 1 WHERE id = 1 ;
-- 1)将2号部门的员工 --- 改为部门编号3

UPDATE dept SET id = 3 WHERE id =2 ;
UPDATE employee SET dept_id = 3 WHERE id =1;


-- sql删除外键约束
-- alter table 表名 drop foreign key 外键名;
ALTER TABLE employee DROP FOREIGN KEY dept_emp_fk ;

INSERT INTO employee(NAME,gender,dept_id) VALUES('张三','男',4) ;
-- sql 添加外键约束
-- alter table 表名 add constraint 外键名称 foreign key 
-- (从表字段名称) references 主表的(主键字段) ;
ALTER TABLE employee 
ADD CONSTRAINT dept_emp_fk 
FOREIGN KEY (dept_id) 
REFERENCES dept(id) ;

级联约束 cascade

级联删除/级联修改 on delete cascade/on update cascade

-- 级联操作cascade
-- 级联删除/级联修改 on delete cascade /on update casade
-- 当修改/删除主表的数据,从表数据随之改动 


-- 将上面这个语句放在外键的后面
-- 添加外键的同时---添加级联修改和级联删除
ALTER TABLE employee 
ADD CONSTRAINT dept_emp_fk 
FOREIGN KEY (dept_id) 
REFERENCES dept(id) 
ON UPDATE CASCADE 
ON DELETE CASCADE ;


-- 直接修改主表数据,
-- 将1号部门改成2号部门,这个时候1号部门的员工,它部门编号变成2
UPDATE dept SET id = 2 WHERE id =1 ;
-- 直接删除主表数据,从表随之变化
-- 将3号部门解散
DELETE FROM dept WHERE id = 3 ;




SELECT * FROM dept ;
SELECT * FROM employee ;


关于表和表的关系/数据库的三大范式(设计数据库)

一对多

​ 用户和订单的关系

​ 一个用户可以有多个订单,

​ 某个订单从属于某个用户

​ 员工和部门的关系

​ 一个部门可以有多个员工

​ 一个员工属于某个部门

多对多

​ 订单表和商品表

​ 一个订单含多个商品,

​ 一个商品被多个订单包含

​ 学生表和选课表

​ 一个学生可以选多个课程,

​ 一个课程可以被多个学生选择

一对一

​ 人和身份证

​ 一个人对应一个身份证

​ 一个身份证对应某个人

多表关系—设计一个库,满足数据库的范式(就是数据库设计的一种规范要求)

数据库的三大范式(设计数据库)

1NF:第一范式 数据库表中的每一列是不能拆分的原子性!(最基本要求)

(表中的每一列都是单独的,不能拆分)

2NF: 第二范式

​ 在满足1NF的前提下

​ 1)一张表描述一件事情

​ 2)非主键字段必须完全依赖于主键字段

3NF:第三范式

​ 在满足2NF的基础上

​ 非主键字段中间不能产生传递依赖

​ A字段----依赖B字段------依赖于C字段 A字段-----依赖C字段

多表查询(使用最多)

笛卡尔乘积

内连接

外连接

子查询:select 嵌套 select …

​ 情况一:单行单列的数据

​ 情况二:多行多列

​ 情况三:通过两个表的关系—查询出结果集------当做"虚表"在和其他之间关联查询!

 CREATE DATABASE ee_2211_02;
 -- 设计一个部门表
 CREATE TABLE dept(
	id INT PRIMARY KEY AUTO_INCREMENT ,-- 部门编号
	NAME VARCHAR(10)                   -- 部门名称
 ) ;
 INSERT INTO dept(NAME) VALUES('开发部'),('市场部'),("销售部") ;
 -- 设计员工表
 CREATE TABLE employee(
	id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
	NAME VARCHAR(10),                  -- 姓名
	gender VARCHAR(3),                 -- 性别
	salary DOUBLE,		           -- 工资
	join_date DATE,                     -- 入职日期
	dept_id INT ,                      -- 部门编号
	-- 设置外键
	CONSTRAINT -- 声明
	dept_emp_fk  -- 外键名
	FOREIGN KEY (dept_id) -- 指定外键名作用从表的指定字段上
	REFERENCES  -- 关联
	dept(id)
	 
 );
 INSERT INTO employee(NAME,gender,salary,join_date,dept_id) VALUES
 ('文章','男',12000.0,'2021-11-11',1),
 ('王宝强','男',8000.0,'2022-05-28',1),
 ('高圆圆','女',7000.0,'2021-12-31',2),
 ('张佳','女',8000.0,'2021-10-30',3) ,
 ('马保国','男',3000.0,'2022-10-15',3),
 ('黄晓明','男',15000.0,'2022-9-30',1),
 ('姚笛','女',6000.0,'2021-10-10',3);

多表查询语法select 字段列表 from 表名1,表名2;

需求:查询员工表和部门表的所有信息
SELECT 
  e.*,
  d.* 
FROM
  employee e,
  dept d ;
/*

​ 上面查询的结果不符合实际要求,7个员工,现在出现了21个
这个就是笛卡尔乘积,针对A表的记录 和B的记录数,两个表的记录相乘
出现笛卡尔乘积的原因:没有设置连接条件!

针对多表查询
1)查询哪张表!
2)查询指定表的哪些字段!
3)他们表和表之间的关系!

多表查之内连接()

隐式内连接:where条件—多去使用这种查!
select	
		字段列表 
        from 
		表名1,表名2...表名n
	where  	
		这些表之间的连接条件;
需求:查询员工表和部门表的所有信息
SELECT 
  e.`id` '员工编号',
  e.`name` '姓名',
  e.`gender` '性别',
  e.`salary` '工资',
  e.`join_date` '入职日期',
  e.`dept_id` '员工所在的部门',
  d.id '部门编号',
  d.`name` '部门名称' 
FROM
  employee e,
  dept d 
WHERE e.`dept_id` = d.`id` ;

内连接—显示内连接 (inner可以省略) join

select
		字段列表
	from 表名1
	(inner) join
	表名2
	on  连接条件
需求:查询员工的姓名,工资,入职日期以及各他的部门名称信息
SELECT 
  e.`name` '姓名',
  e.`salary` '工资',
  e.`join_date` '入职日期',
  d.`name` '部门名称' 
FROM
  employee e 
  JOIN dept d 
    ON e.`dept_id` = d.`id` ;

外连接

左外连接(通用)

将A表(左表)的和B表(右表)的交集以及A表(左表)中所有数据全部查询!

select 
	字段列表
	from
		左表名
	left (outer) join    -- outer可以省略   
			  -- righter (outer) join 可以右外连接
	

表名2
on
	连接条件;
查询所有的员工信息以及员工所在的部门名称信息,没有部门员工的也得查出来
select 
	e.*,
	d.`name` '部门名称'
from 
	employee e,
	dept d
where 
	e.`dept_id` = d.`id` ;

*/
SELECT 
  e.*,
  d.`name` "部门名称" 
FROM
  employee e 
  LEFT OUTER JOIN dept d    -- outer可以省略
    ON e.`dept_id` = d.`id` ;

多表查询之子查询

就是select去嵌套select
情况1:使用where 后面条件表达式=,<=,>=…

需求:查询员工工资最高的员工信息以及他的部门信息

– 1)查询最高工资是多少

SELECT MAX(salary) FROM employee ; -- 15000
-- 2)是15000的员工信息以及部门门信息
SELECT 
  e.*,
  d.`name` '部门名称' 
FROM
  employee e,
  dept d 
WHERE e.`dept_id` = d.`id` 
  AND e.`salary` = 15000 ;

 -- 上面的sql一步走
SELECT 
  e.*,
  d.`name` '部门名称' 
FROM
  employee e,
  dept d 
WHERE e.`dept_id` = d.`id` 
  AND e.`salary` = 
  (SELECT 
    MAX(salary) 
  FROM
    employee) ;
查询大于平均工资所有员工信息以及部门名称信息
平均工资是多少
SELECT AVG(salary) FROM employee ; -- 8625
查询员工表中员工工资大于8625的员工信息以及部门名称信息,没有部门的员工必须查(外连接)
SELECT 
    e.*,
    d.`name` '部门名称'
FROM
employee e,
dept d 
WHERE 
    e.`dept_id` = d.`id` 
    AND 
    e.`salary` > 8625;
    
    
-- 上面sql优化--一步走,查询出没有部门的员工---使用外连接
SELECT 
  e.*,
  d.`name` '部门名称' 
FROM
  employee e,
  dept d 
WHERE e.`salary` > 
  (SELECT 
    AVG(salary) 
  FROM
    employee) 
  AND e.`dept_id` = d.`id` ;
情况2:利用in集合语句 (使用最多)
需求:查询在"市场部"和销售部的员工信息
-- 分步走
-- 1)查询市场部和销售部的部门编号

SELECT 
  id 
FROM
  dept 
WHERE NAME = '市场部' 
  OR NAME = '销售部' ;
-- 2)查询员工表中在2或者3号部门的员工信息以及部门信息
SELECT 
  e.*,
  d.`name` '部门名称'
FROM 
 employee e,
 dept d
WHERE 
    e.`dept_id` = d.`id` 
    AND 
    e.`dept_id` IN(2,3) ;

  -- 一步走
SELECT 
  e.*,
  d.`name` '部门名称' 
FROM
  employee e,
  dept d 
WHERE e.`dept_id` = d.`id` 
  AND e.`dept_id` IN 
  (SELECT 
    id 
  FROM
    dept 
  WHERE NAME = '市场部' 
    OR NAME = '销售部') ;
查询某个表的结果集----->

当前"虚表"继续和其他进行关联查询(按照连接条件)

查询入职日期大于"2021-11-11"后入职的员工信息以及部门名称信息
分步走
-- 1)查询入职日期大于"2021-11-11"后入职的员工信息
SELECT
*
FROM employee  e
WHERE 
	e.`join_date` > '2021-11-11' ;
-- 2)在1)的结果集的基础上和部门表进行关联查询
SELECT 
  t.name '员工姓名',
  t.join_date '入职日期',
  d.`name` '部门名称' 
FROM
  (SELECT 
    * 
  FROM
    employee e 
  WHERE e.`join_date` > '2021-11-11') t LEFT JOIN 
  dept d 
 ON  t.dept_id = d.id ;   -- 左外连接,没有员工的部门需要查询


 -- 另一种写法(不考虑没有部门的员工)
SELECT 
  e.*,
  d.`name` '部门名称' 
FROM
  employee e,
  dept d 
WHERE e.`join_date` > '2021-11-11' 
  AND e.`dept_id` = d.`id` ;

 


SELECT * FROM dept	 ;

SELECT * FROM employee ;

数据库的事务/事务的特点/事务的隔离级别

什么是数据库事务?

在一个业务中执行多个sql(多张表的sql),这多个sql句要么同时执行成功,
要么同时执行失败!

​ 举例
​ 转账操作 多个账户同时操作,一个减钱,一个加钱
​ 提交订单
​ 订单表和订单项表 同时添加数据

-- 创建一个账户表
CREATE TABLE account(
   id INT PRIMARY KEY AUTO_INCREMENT , -- 账户id
   NAME VARCHAR(10), -- 账户名称
   balance INT -- 余额
);

INSERT INTO account (NAME,balance) VALUES('zhangsan',1000),('lisi',1000);

-- zhangsan给lis转账500

-- 没有数据库事务管理转账操作
/*
update account set balance = balance - 500 where id = 1 ;

-- 转账出问题了 -- 这块不执行了,前面执行了!

update account set balance = balance + 500 where id = 2; 
*/

-- 使用数据库事务管理整个转账的业务操作

-- 开启事务
START TRANSACTION ;

-- 操作多个sql语句或多张表的sql(同时增删改)
UPDATE account SET balance = balance - 500 WHERE id = 1;
-- 出问题了
UPDATE account SET balance = balance + 500 WHERE id = 2 ;

-- 事务回滚
ROLLBACK ;

-- 提交事务
COMMIT;

UPDATE account SET balance = 1000 ;

-- 查询账户
SELECT * FROM account ;

传统事务特点:ACID(关系型数据库传统事务)

​ 原子性 : 在使用事务管理的时候,执行多个sql增删改,要么同时在执行,要么同时失败

​ 一致性:高并发的时候,需要保证事务多次读写,保证事物一致性!

​ 隔离性:事务和事务之间是独立的,相互不影响

​ 举例
​ 购物车业务—>购物车表和购物车项表同时添加数据
​ 订单业务---->订单表和订单项表同时添加数据

​ 持久性:事务一旦提交,对数据的影响是永久性的,即使关机了,数据也是要更新的

什么是数据库事务:

在一个业务中执行多个sql

mysql 中如何管理事务 ----单独这种指令

start transaction :开启事务----在操作多个sql语句之前,将整个业务操作管理起来

​ 自动提交事务切换手动提交事务

​ 执行你的业务操作:多个sql语句或者多张表的sql(增删改)

​ 如果中间出现了问题,可以回滚------rollback: 默认 回滚到sql之前的状态

​ 然后提交事务 commit :事务不提交

事务的隔离级别:

四个级别: 从小到大,安全性—从低到高,效率性:从高到低

第一种级别:

​ read uncommitted : 读未提交 会造成问题"脏读"

​ 脏读: 是事务管理最严重的问题:一个事务读取到另一个事务没有提交的事务;

第二种级别:

​ read committed : 读已提交 有效防止脏读,出现一个问题"不可重复读";

​ 事务(当前本身这个事务)多次读取到的事务不一致!

第三种级别:mysql默认级别

​ repeatable read : 可重复读,有效防脏读,不可重复读,出现幻读(一般有更新操作影响)

第四种级别:

​ serializable : 串行话 (一个事务读取到另一个没提交的事务,数据查不到,这个事物必须提交,才能查到事务)

JDBC

JDBC:java数据库连接,是一种规范!

七大步骤

1)导包驱动包

2)注册驱动–加载驱动类

3)获取数据库的连接对象java.sql.Connection

4)准备sql语句

5)通过Connection连接对象获取数据库的执行对象

6)执行sql语句

7)释放资源

public class JdbcDemo {
public static void main(String[] args) throws Exception {
    

//Jdbc的操作:java连接数据库
//1)导入驱动jar包
//2)注册驱动---加载驱动类
Class.forName("com.mysql.jdbc.Driver") ; //mysql5.5或者5.1的jar包:都是这个全限定名称
                                        //mysql8.0jar包: com.mysql.cj.jdbc.Driver

//3)获取数据库连接对象
//DriverManager驱动管理类--->
//public static Connection getConnection(String url, 连接库地址  (统一资源定位符)
//                                       String user, mysql的用户名  root用户
//                                       String password) 登录MySQL的密码
//                                throws SQLException

//url--->组成: 协议名称 ://域名:端口号/具体的路径
/*

* mysql的驱动jar包如果是8.0以后:url的后面           编码格式         是否启用证书登录     服务器时区         是否公钥模式
* jdbc:mysql://localhost:3306/ee_2211_02?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
* */

//  jdbc:mysql://localhost:3306/库名
Connection conn = DriverManager.getConnection(
        "jdbc:mysql://localhost:3306/ee_2211_02",
        "root",
        "123456"
);

//4)准备sql语句
String sql = "insert into account (name,balance) values('高圆圆',1000)" ;

//5)通过数据库连接对象Connection获取数据库的执行对象
//执行静态sql语句
//Statement createStatement()throws SQLException创建一个Statement对象,用于将SQL语句发送到数据库
Statement stmt = conn.createStatement();

//6)执行sql语句
//Statement--->
//int executeUpdate(String sql)throws SQLException 通用的更新操作
int count = stmt.executeUpdate(sql);
System.out.println("影响了"+count+"行");

//7)释放资源
stmt.close();
conn.close();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值