JDBC-JDBC 的四种驱动类型

JDBC 有四种驱动类型,分别是:

  • JDBC-ODBC 桥(JDBC-ODBC bridge driver plus ODBC driver)
  • 本地 API 驱动(Native-API partly Java driver)
  • 网络协议驱动(JDBC-Net pure Java driver)
  • 本地协议驱动(Native-protocol pure Java driver)

JDBC-ODBC 桥

从名称之中就可以知道,这种驱动是 JDBC 在 ODBC 已有的功能上做了一层适配工作,即搭桥(Bridge)。

这种驱动完全依赖 ODBC 的实现,JDBC 只是做了一层封装工作。

JDBC-ODBC 桥最主要的功能,是支持 Java 访问 Access 这种伪关系型数据库。

JDBC-ODBC 桥最大的优点在于:ODBC 的代码在天然的在许多使用数据库的客户端上有,所以这种驱动的安装十分便捷。

但是,JDBC-ODBC 桥有以下两个主要的缺点:

  • 由于对 ODBC 的依赖,导致支持的功能有限,性能不佳,扩展能力很弱。
  • 不适合在并发访问数据库的情况下使用。

在 Java8 的实现中,已经删除了 JDBC-ODBC 桥这种驱动方式。

本地 API 驱动

这种驱动方式,相当于 JDBC 完全使用了 ODBC 的工作方式。

在这种驱动中,JDBC调用转换为对 DBMS 的客户端 API 的调用。

JDBC-ODBC 桥的驱动方式,等于是对所有的数据库调用进行了一次整合;而本地 API 驱动则是绕过了 ODBC,对数据库 API 的调用进行了整合。

所以这种驱动方式的效率,相比第一种驱动,会有显著的提高。

然而,与 JDBC-ODBC 桥驱动相同,对客户端数据库的 API 有依赖,所以也不适合基于 Internet 的应用。

网络协议驱动

这种驱动方式,适用于基于 Internet 的应用,它依赖于一个网络服务器上的中间产品:

  • 首先,将 JDBC 调用转化为一个 DBMS 无关的网络协议,传递给中间服务器。
  • 然后,中间服务器负责将这种请求,转化为符合相应的数据库规范的请求,转发给数据库服务器。

这种驱动是基于 Server 端的,所以不需要在客户端加载数据库厂商的代码库。

它在在执行效率,可升级性,灵活性方面都很好。

这种驱动,设计的代码量很小,可以很快地加载到内存当中。

但是,这种驱动在中间件层仍然需要有配置其它数据库驱动程序,而且增加了一个中间层去传递数据,执行效率还不是最好。

同时,基于 Internet 的访问要求,客户端必须处理 Web 所提出的安全性、通过防火墙的访问等方面的额外要求。

本地协议驱动

这种驱动程序将 JDBC 调用直接转换为 DBMS 使用的网络协议。

这种驱动允许从客户端机器直接调用 DBMS 服务器,是内部网(Intranet)访问的优秀解决方案。

由于许多这样的协议都是专用的,因此数据库提供者自己将是主要来源。

  1. 总结
  • JDBC-ODBC 桥,适合作为开发应用时的一种过度方案,不使用大量的数据库操作。
  • 本地 API 驱动,只适用于内部网,但是执行效率不高,所以一般也不采用。
  • 网络协议驱动,适用于需要同时连接多个不同种类的数据库的应用。
  • 本地协议驱动,适合于连接单一数据库的应用。
    从执行效率来讲,第3、4型的驱动是首选,平时通过加载一个 jar 包来达到连接数据库目的的,是第4型驱动。

JDBC-基础

什么是 JDBC

JDBC,Java Database Connectivity(Java 数据库连接),是一组执行 SQL 语句的 Java API。

JDBC,是 Java SE(Java Platform, Standard Edition)标准的一部分。

Java 程序可以通过 JDBC 连接到关系型数据库,并且使用 SQL(Structured Query Language,结构化查询语言)完成对数据库的操作。

我们开发时常用的 ORM 框架(Object Relational Mapping),例如 Hibernate,MyBatis,其本质就是对 JDBC 的一种封装。

JDBC 驱动和面向接口编程

Java API,就是接口(interface),所以说 JDBC 只给出了接口,没有提供实现类。

由各个数据库的厂商提供 JDBC 的实现,这些实现类就是我们口中常说的:驱动程序。

JDBC 的编程工作,是需要面向标准的 JDBC API,不需要关心使用的数据库到底是什么。

使用 Oracle,DB2,还是 MyBatis 对于 JDBC 的编程都没有影响,这就是面向接口编程。

理论上说,如果需要切换数据库,只需要换一个驱动程序就可以了,所以说,JDBC 具有跨数据库的特性。

当然实际操作上没有这么简单,因为 JDBC 跨数据库的特性是基于全部使用标准的 SQL 语句,而某些数据库会有一些只有自己才能用的特殊 SQL 语法。

例如,Oracle 的 rowid、rownum,MySQL 的 limit。

JDBC 组成和功能

JDBC的总体结构有四个组件:应用程序、驱动程序管理器、驱动程序和数据源。

JDBC 主要有以下三个功能:

  • 建立程序与数据库的连接。
  • 执行 SQL 语句。
  • 获得 SQL 语句的执行结果。

JDBC 和 ODBC

ODBC,Open Database Connectivity(开放数据库连接),也是一组通过 API 访问数据库的技术。

ODBC 先于 JDBC 的出现,JDBC 模仿了 ODBC 的设计。

与 JDBC 相同,ODBC 需要数据库厂商提供驱动,支持数据库之间的切换,而 ODBC 负责管理数据库驱动。

相比于 ODBC,JDBC 有以下优势:

  • JDBC 对于数据库的操作更加简单、直观。
  • JDBC 具有更高的安全性。

Spring中注册Bean的方式有哪些?

一、概述

我们知道,在使用Spring框架后,Bean统一都交给IOC容器去管理和创建,那么将一个对象加入到Spring容器中有哪些方式呢,今天我们结合具体的代码来总结一下。

二、第一种方式: XML配置方式

XML配置的方式,是Spring最早支持的方式,不过现在XML方式已经用的比较少了,基本上都是用后面的配置方式替代了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Student {
}

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.wsh.injectbean.method_01.Student"/>
</beans>

/**
* 第一种方式: XML文件配置
*/
public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
System.out.println(applicationContext.getBean("student"));
}
}

三、第二种方式: 使用@Component注解 + @ComponentScan包扫描方式

为了解决bean太多时,XML文件过大,从而导致膨胀不好维护的问题。在Spring2.5中开始支持:@Component、@Repository、@Service、@Controller等注解定义bean。@Component放在类名上面,然后通过@ComponentScan指定一个路径,Spring进行扫描带有@Componet注解的bean,然后加至容器中。

注意,这种方式不单单指的是@Component注解,还包括@Controler、@Service、@Repository等派生的注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Component
public class UserHandler {
}

@Service
public class UserService {
}

@Repository
public class UserDao {
}

@Controller
public class UserController {
}

@ComponentScan("com.wsh.injectbean.method_02")
@Configuration
public class AppConfig {
}

/**
* 第二种方式: 使用@Component注解 + @ComponentScan包扫描方式
* 包括@Controler@Service@Repository等派生的注解。
* 为了解决bean太多时,xml文件过大,从而导致膨胀不好维护的问题。在spring2.5中开始支持:@Component@Repository@Service@Controller等注解定义bean。
* 其实本质上@Controler@Service@Repository也是使用@Component注解修饰的。
* <p>
* 通常情况下:
*
* @Controller:一般用在控制层
* @Service:一般用在业务层
* @Repository:一般用在持久层
* @Component:一般用在公共组件上
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("userDao"));
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userController"));
System.out.println(applicationContext.getBean("userHandler"));
}
}

四、第三种方式:@Configuration + @Bean方式

这种方式其实也是我们最常用的方式之一,@Configuration用来声明一个配置类,然后使用 @Bean 注解声明一个bean,将其加入到Spring容器中。

通常情况下,如果项目中有使用到第三方类库中的工具类的话,我们都是采用这种方式注册Bean。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Student {
}

@Configuration
public class AppConfig {

@Bean
public Student student() {
return new Student();
}

}

/**
* 第三种方式:@Configuration + @Bean方式
* <p>
* 通常情况下,如果项目中有使用到第三方类库中的工具类的话,我们都是采用这种方式注册Bean
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("student"));
}
}

五、第四种方式:FactoryBean方式

FactoryBean是一个Bean,它允许我们自定义Bean的创建,主要有三个方法:

  1. getObject():自定义Bean如何创建;
  2. getObjectType():要注册的Bean的类型;
  3. isSingleton():是否单例;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class User {
}

@Component
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}

@Override
public Class<?> getObjectType() {
return User.class;
}

@Override
public boolean isSingleton() {
return true;
}
}

@Configuration
@ComponentScan("com.wsh.injectbean.method_04")
public class AppConfig {

}

/**
* 第四种方式:使用FactoryBean
*
* FactoryBean在一些框架整合上用的比较多,如Mybatis与Spring的整合中:MapperFactoryBean、SqlSessionFactoryBean等
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("userFactoryBean"));
System.out.println(applicationContext.getBean("&userFactoryBean"));
}
}

六、第五种方式:@Import方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Student {
}

@Import({Student.class})
@Configuration
public class AppConfig {

}

/**
* 第五种方式:@Import方式
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println("================");
System.out.println(applicationContext.getBean("com.wsh.injectbean.method_05.Student"));
System.out.println(applicationContext.getBean("student"));
}
}

七、第六种方式:@Import + ImportSelector方式

首先介绍一下ImportSelector接口的好处,主要有以下两点:

  1. 把某个功能的相关类放到一起,方面管理和维护。

  2. 重写selectImports方法时,能够根据条件判断某些类是否需要被实例化,或者某个条件实例化这些bean,其他的条件实例化那些bean等,我们能够非常灵活的定制化bean的实例化。

这种方式我们需要实现ImportSelector接口,并重写selectImports()方法,然后将我们要导入的类的全限定名写在里面即可。

指定需要定义bean的类名,注意要包含完整路径,而非相对路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Product {
}

public class User {
}

public class MyImportSelector implements ImportSelector {

// 指定需要定义bean的类名,注意要包含完整路径,而非相对路径
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.wsh.injectbean.method_06.Product", "com.wsh.injectbean.method_06.User"};
}

}

@Import({MyImportSelector.class})
@Configuration
public class AppConfig {

}

/**
* 第六种方式:@Import + ImportSelector方式
* <p>
* ImportSelector接口的好处主要有以下两点:
* <p>
* 1.把某个功能的相关类放到一起,方面管理和维护。
* 2.重写selectImports方法时,能够根据条件判断某些类是否需要被实例化,或者某个条件实例化这些bean,其他的条件实例化那些bean等,我们能够非常灵活的定制化bean的实例化。
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println("================");
System.out.println(applicationContext.getBean("com.wsh.injectbean.method_06.Product"));
try {
System.out.println(applicationContext.getBean("product"));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("================");
System.out.println(applicationContext.getBean("com.wsh.injectbean.method_06.User"));
try {
System.out.println(applicationContext.getBean("user"));
} catch (Exception e) {
e.printStackTrace();
}
}
}

八、第七种方式:@Import + ImportBeanDefinitionRegistrar方式?

这种方式我们需要实现ImportBeanDefinitionRegistrar接口,并重写registerBeanDefinitions()方法,然后定义我们需要注册的Bean的定义信息,然后registry.registerBeanDefinition()方法注册即可。这种方式比ImportSelector更加灵活,可以自定义bean的名称、作用域等很多参数。 像我们常见的Spring Cloud中的Feign,就使用了ImportBeanDefinitionRegistrar,具体可以参考FeignClientsRegistrar类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class User {
}


public class Product {
}

public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 可以自定义bean的名称、作用域等很多参数
registry.registerBeanDefinition("user", new RootBeanDefinition(User.class));
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Product.class);
rootBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
registry.registerBeanDefinition("product", rootBeanDefinition);
}
}

@Import({CustomImportBeanDefinitionRegistrar.class})
@Configuration
public class AppConfig {

}

/**
* 第七种方式:@Import + ImportBeanDefinitionRegistrar方式
*
* 可以自定义bean的名称、作用域等很多参数, 像我们常见的Spring Cloud中的Feign,就使用了ImportBeanDefinitionRegistrar
* class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
* public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
* this.registerDefaultConfiguration(metadata, registry);
* this.registerFeignClients(metadata, registry);
* }
* }
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("product"));
System.out.println(applicationContext.getBean("user"));
}
}