JDBC的全称是Java DataBase Connection,也就是Java数据库连接,我们可以⽤它来操作关系型数据库。JDBC接⼝及相关类在java.sql 包和javax.sql包⾥。我们可以⽤它来连接数据库,执⾏SQL查询,存储过程,并处理返回的结果。
JDBC接⼝让Java程序和JDBC驱动实现了松耦合,使得切换不同的数据库变得更加简单。
1.com.mysql.cj.jdbc.Driver是Driver驱动所在的位置,加载驱动
2.Class.forName()是一个反射,但是他没有返回一个Class对象,因为我们不需要;
这是Driver的代码:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
它除了构造方法,就只有一个静态代码块,当我们反射进行的时候,这个类就开始初始化,他的静态代码块内容就已经被执行了,我们真正需要的是DriverManager.registerDriver(new Driver());这一行代码
是一个工厂类,我们通过它来创建数据库连接,当JDBC的Driver类被加载进来时,它会自己注册到DriverManager类里面
1.注册驱动
2.获取连接
3.创建一个Statement语句对象
4.执行SQL语句
5.处理结果集
6.关闭资源
1.PreparedStatement 继承于 Statement,Statement 一般用于执行固定的没有参数的SQL。2.PreparedStatement 一般用于执行有?参数预编译的SQL语句。可以防止SQL注入,安全性高于Statement。3.CallableStatement适用于执行存储过程。
1)Statement的execute(String query)⽅法⽤来执⾏任意的SQL查询,如果查询的结果是⼀个ResultSet,这个⽅法就返回true。如果结果不是ResultSet,⽐如insert或者update查询,它就会返回false。
2)Statement的executeQuery(String query)接⼝⽤来执⾏select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使⽤executeQuery来执⾏查询语句,这样的话如果传进来的是insert或者update语句的 话,它会抛出错误信息为“executeQuery method can not be used for update”的java.util.SQLException。
3)Statement的executeUpdate(String query)⽅法⽤来执⾏insert或者update/delete(DML)语句。
4)只有当你不确定是什么语句的时候才应该使⽤execute()⽅法,否则应该使⽤executeQuery或者executeUpdate⽅法。
最好的办法是利用sql语句进行分页,这样每次查询出的结果集中就只包含某页的数据内容。
sql语句分页,不同的数据库下的分页方案各不一样,假设一共有38条数据,每页有10条数据,查询第3页的数据,下面是主流的三种数据库的分页sql:
Oracle:
select * from
(select *,rownum as tempid from student ) t
where t.tempid between 20 and 30;
mysql:
select * from students limit 20,10;
sql server:
select top 10 * from students where id not in
(select top 20 id from students order by id)
order by id;
事务是作为单个逻辑⼯作单元执⾏的⼀系列操作,⼀个逻辑⼯作单元必须有四个属性,称为原⼦性、⼀致性、隔离性和持久性(ACID) 属性,只有这样才能成为⼀个事务 。JDBC处理事务有如下操作:
conn.setAutoComit(false);设置提交⽅式为⼿⼯提交。
conn.commit()提交事务。
conn.rollback()回滚事务。
提交与回滚只选择⼀个执⾏。正常情况下提交事务,如果出现异常,则回滚。
数据库连接是⼀种关键的有限的昂贵的资源,对数据库连接的管理能显著影响到整个应⽤程序的伸缩性和健壮性,影响到程序 的性能指标。数据库连接池正是针对这个问题提出来的。
数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接,⽽不是重新建⽴⼀个;释放空闲时间超过最⼤空闲时间的数据库连接来避免因为没有释放数据库连接⽽引起的数据库连接遗漏。这项技术能明显提⾼对数据库操作的性能。
数据库连接池在初始化时将创建⼀定数量的数据库连接放到连接池中,这些数据库连接的数量是由最⼩数据库连接数来设定的。⽆论这些数据库连接是否被使⽤,连接池都将⼀直保证⾄少拥有这么多的连接数量。连接池的最⼤数据库连接数量限定了这个连接池能占有的最⼤连接数,当应⽤程序向连接池请求的连接数超过最⼤连接数量时,这些请求将被加⼊到等待队列中。
桥接模式,首先DriverManager获得Connection是通过反射和类加载机制从数据库驱动包的driver中拿到连接,所以这里真正参与桥接模式的是driver,而DriverManager和桥接模式没有关系,DriverManager只是对driver的一个管理器。而我们作为使用者只去关心Connection,不会去关心driver,因为我们的操作都是通过操作Connection来实现的。这样分析下来这个桥接就清晰了逻辑——java.sql.Driver作为抽象桥类,而驱动包如com.mysql.jdbc.Driver具体的实现桥接类,而Connection是被桥接的对象。
默认情况下,我们创建的数据库连接,是工作在自动提交的模式下的。这意味着只要我们执行完一条查询语句,就会自动进行提交。因此我们的每条查询,实际上都是一个事务,如果我们执行的是DML或者DDL,每条语句完成的时候,数据库就已经完成修改了。有的时候我们希望由一组SQL查询组成一个事务,如果它们都执行OK我们再进行提交,如果中途出现异常了,我们可以进行回滚。
JDBC接口提供了一个setAutoCommit(boolean flag)方法,我们可以用它来关闭连接自动提交的特性。我们应该在需要手动提交时才关闭这个特性,不然的话事务不会自动提交,每次都得手动提交。数据库 通过表锁来管理事务,这个操作非常消耗资源。因此我们应当完成操作后尽快的提交事务。在这里有更多关于事务的示例程序。
CLOB意思是Character Large OBjects,字符大对象,它是由单字节字符组成的字符串数据,有自己专门的代码页。这种数据类型适用于存储超长的文本信息,那些可能会超出标准的VARCHAR数据类型长度限制(上限是32KB)的文本。
BLOB是Binary Larget OBject,它是二进制大对象,由二进制数据组成,没有专门的代码页。它能用于存储超过VARBINARY限制(32KB)的二进制数据。这种数据类型适合存储图片,声音,图形,或者其它业务程序特定的数据。
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 Class.forName,这种方式来控制类的加载,该方法会返回一个 Class 对象。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
(1)Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
(2)Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
(3)Constructor :可以用 Constructor 创建新的对象。
应用举例:工厂模式,使用反射机制,根据全限定类名获得某个类的 Class 实例。
反射是用来描述类的信息的。对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
class:用来描述类本身
Packge:用来描述类所属的包
Field:用来描述类中的属性
Method:用来描述类中的方法
Constructor:用来描述类中的构造方法
Annotation:用来描述类中的注解
1)Class clazz=class.forName("包名.类名")
2)Class clazz=类名.class;
3)Class clazz=对象.getClass();
(1)获取类的权限修饰符--------->int result=getModifiers();
(2)获取名字------------>string name=clazz.getName();
(3)获取包名------------>Packge p=clazz.getPackge();
(4)寻找clazz中无参数构造方法:Clazz.getConstructor([String.class]);
执行构造方法创建对象:Con.newInstance([参数]);
(5)Field c=cls.getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
Field c=cls.getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和 proteced,但是不包括父类的声明字段。
(1)反射得经典用法就是在xml或者properties配置文件中,然后在java类里面区解析这些内容,得到一个字符串,然后通过反射机制,通过这些字符串获得某个类得class实例,这样的话就可以动态的配置一些东西,而不需要每次都重新去new,要改的话也是直接改配置文件,代码维护起来方便很多。
(2)当你在做一个软件开发的插件的时候,你连插件的类型名称都不知道,你怎么实例化这个对象呢?因为程序是支持插件的(第三方的),在开发的时候并不知道 。所以无法在代码中 New出来 ,但反射可以,通过反射,动态加载程序集,然后读出类,检查标记之后再实例化对象,就可以获得正确的类实例。
(3)在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new ClassName(),而必须用到反射才能创建这个对象.反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。很多工厂模式就是使用的反射。
getSimpleName:只获取类名
getName:类的全限定名,jvm中Class的表示,可以用于动态加载Class对象,例如Class.forName。
getCanonicalName:返回更容易理解的表示,主要用于输出(toString)或log打印,大多数情况下和getName一样,但是在内部类、数组等类型的表示形式就不同了。