# 数据源与注解

这里主要讲Spring数据源的集成以及注解的使用

# 连接数据源

我们使用数据连接池来连接数据源,数据库连接池的作用为:

  • 数据源(连接池)是提高程序性能如出现的
  • 事先实例化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕后将连接资源归还给数据源

常见的连接池有DBCP、C3P0、BoneCP、Druid。

那么连接步骤为:

  • 导入数据源的坐标和数据库驱动坐标
  • 创建数据源对象
  • 设置数据源的基本连接数据
  • 使用数据源获取连接资源和归还连接资源

现在先导入依赖坐标,pom.xml文件内容:

<dependencies>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>2.7.2</version>
</dependency>
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
</dependencies>
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

# java直接连接

使用java代码直接连接:

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;

@Test
public void test1() throws Exception {
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass("org.mariadb.jdbc.Driver");
    dataSource.setJdbcUrl("jdbc:mariadb://10.0.10.150:6612/test");
    dataSource.setUser("root");
    dataSource.setPassword("666666");

    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();

}

@Test
public void test2() throws Exception {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("org.mariadb.jdbc.Driver");
    dataSource.setUrl("jdbc:mariadb://10.0.10.150:6612/test");
    dataSource.setUsername("root");
    dataSource.setPassword("666666");
    
    DruidPooledConnection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}
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

# 配置文件连接

下面是配置文件jdbc.properties的内容:

jdbc.driver=org.mariadb.jdbc.Driver
jdbc.url=jdbc:mariadb://10.0.10.150:6612/test
jdbc.username=root
jdbc.password=666666
1
2
3
4

使用该配置文件连接:

import java.util.ResourceBundle;

@Test
//使用配置文件连接数据库
public void test3() throws Exception {
    ResourceBundle rb = ResourceBundle.getBundle("jdbc");
    String driver = rb.getString("jdbc.driver");
    String url = rb.getString("jdbc.url");
    String username = rb.getString("jdbc.username");
    String password = rb.getString("jdbc.password");
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setJdbcUrl(url);
    dataSource.setDriverClass(driver);
    dataSource.setUser(username);
    dataSource.setPassword(password);

    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Spring连接数据源

Spring可以把信息写到applicationContext.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="org.mariadb.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mariadb://10.0.10.150:6612/test"></property>
        <property name="user" value="root"></property>
        <property name="password" value="666666"></property>
    </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12

使用该xml连接数据源:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Test
//测试Spring容器连接数据源
public void test4() throws Exception {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    DataSource dataSource = app.getBean(DataSource.class);
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}
1
2
3
4
5
6
7
8
9
10
11
12

也可以使用applicationContext加载配置文件来连接数据源,其内容修改为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--    加载配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# JdbcTemplate

它是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。

使用JdbcTemplate开发步骤如下:

  1. 导入spring-jdbc和spring-tx坐标
  2. 创建数据库表和实体
  3. 创建JdbcTemplate对象
  4. 执行数据库操作

使用spring jdbctemplate连接数据库,pom.xml中添加:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.2.12.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.2.12.RELEASE</version>
</dependency>
1
2
3
4
5
6
7
8
9
10

测试一下链接:

package com.muyun.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;

import java.beans.PropertyVetoException;

public class JdbcTemplateTest {

    @Test
    public void test1() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("org.mariadb.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mariadb://10.0.10.150:6612/test");
        dataSource.setUser("root");
        dataSource.setPassword("666666");
        //设置数据源对象
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        int row = jdbcTemplate.update("insert into account values(?,?)","li",5000);
        System.out.println(row);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

我们可以将JdbcTemplate的创建权交给Spring,将数据源DataSource的创建权也交给Spring,在Spring容器内部将数据源DataSource注入到JdbcTemplate模版对象中,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--加载配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置组件扫描-->
    <context:component-scan base-package="com.muyun"/>

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

再进行测试:

@Test
public void test2() {
   ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
   JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);
   int row = jdbcTemplate.update("insert into account values(?,?)","zhang",3000);
   System.out.println(row);
}
1
2
3
4
5
6
7

下面使用jdbcTemplate进行简单查询:

package com.muyun.test;

import com.muyun.domain.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testUpdate(){
        jdbcTemplate.update("update account set money=? where name=?",999,"li");
    }

    @Test
    public void testDelete(){
        jdbcTemplate.update("delete from account where name=?","li");
    }

    @Test
    public void testQueryAll(){
        List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
        System.out.println(accountList);
    }

    @Test
    public void testQueryOne(){
        Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class),"li");
        System.out.println(account);
    }

    @Test
    public void testQueryCount(){
        Long aLong = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
        System.out.println(aLong);
    }
}
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

# 注解开发

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。注解分为原始注解和新注解。

# 原始注解

Spring的原始主要替代<Bean>的配置:

注解名 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化Bean
@Service 使用在service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入
@Resource 相当于@Autowired+@Qualifier,按照名称进行注入
@Value 注入普通属性
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法

# 使用注解

我们之前在applicationContext.xml中进行注入,也可以使用注解进行注入:

原来的配置为:

<bean id="userDao" class="com.muyun.dao.impl.UserDaoImpl"></bean>

<bean id="userService" class="com.muyun.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>
1
2
3
4
5

使用注解替代UserDaoImpl.java中内容为:

@Component("userDao")
public class UserDaoImpl  implements UserDao {

    @Override
    public void save() {
        System.out.println("save method!");
    }
}
1
2
3
4
5
6
7
8

UserServiceImpl.java中内容为:

@Component("userService")
public class UserServiceImpl implements UserService {

    @Autowired //按照数据类型从Spring容器中进行匹配的,如果有多个需要按照名称来匹配,就是下面
    @Qualifier("userDao") //按照名称id从容器中进行匹配的,但是主要此处@Qualifier要结合@Autowired一起使用
    //@Resource(name="userDao") //@Resource相当于@Qualifier+@Autowired
    private UserDao userDao;

    /*使用注解set方法可以注释掉
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }*/

    @Override
    public void save() {
        userDao.save();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

上面配置完后,还需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。在该文件中添加:

<context:component-scan base-package="com.muyun"/>
1

由于Component并不能很好表明该类是属于哪一层,Spring又提供了Controller,Service,Repository来使用,它们和Component一样,只不过更语义化。

也可以通过注解来引用applicationContext.xml中的信息:

@Value("${jdbc.username}")
private String testName;
1
2

通过注解控制类的Scope:

@Service("userService")
@Scope("singleton")
public class UserServiceImpl implements UserService {
}
1
2
3
4

通过注解来说明初始化方法:

@PostConstruct
public void init(){
    System.out.println("Service对象初始化方法!");
}
1
2
3
4

通过注解来说明销毁方法:

@PreDestroy
public void destroy(){
    System.out.println("Service对象的销毁方法!");
}
1
2
3
4

手动关闭容器来触发销毁方法:

public class UserController {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = app.getBean(UserService.class);
        userService.save();
        app.close();
    }
}
1
2
3
4
5
6
7
8

# 新注解

使用原始注解不能替代全部xml配置文件,比如:

  • 非自定义的Bean的配置:<bean>
  • 加载properties文件的配置:<context:property-placeholder>
  • 组件扫描的配置:<context:component-scan>
  • 引入其他文件:<import>

这个时候就可以使用Spring新注解:

注解名字 说明
@Configuration 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定 Spring 在初始化容器时要扫描的包。作用和在 Spring 的 xml 配置文件中的<context:component-scan base-package="com.muyun"/>一样
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource 用于加载.properties 文件中的配置
@Import 用于导入其他配置类

# 使用新注解

创建SpringConfiguration.java文件:

package com.muyun.config;

import org.springframework.context.annotation.*;


//标志该类是Spring的核心配置类,相当于<context:component-scan base-package="com.muyun"/>
@Configuration
@ComponentScan("com.muyun")
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {
}
1
2
3
4
5
6
7
8
9
10
11

创建DataSourceConfiguration.java文件:

package com.muyun.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import java.beans.PropertyVetoException;

//下面加载配置文件,相当于<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器中
    public ComboPooledDataSource getDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        System.out.println(driver);
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
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

使用该配置:

@Test
public void test5() throws PropertyVetoException, SQLException {
    ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    DataSource dataSource = app.getBean(DataSource.class);
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}
1
2
3
4
5
6
7
8

# Spring集成Junit

原始Junit测试Spring时会发现在每个测试中都有:

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = app.getBean(DataSource.class);
1
2

上面是要获取容器,又不能不写,这会造成冗余,为了解决这个问题:

  • 让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
  • 将需要进行测试Bean直接在测试类中进行注入

Spring集成Junit步骤:

  1. 导入spring集成Junit的坐标
  2. 使用@Runwith注解替换原来的运行期
  3. 使用@ContextConfiguration指定配置文件或配置类
  4. 使用@Autowired注入需要测试的对象
  5. 创建测试方法进行测试

pom.xml中添加依赖:

 <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
   <scope>test</scope>
 </dependency>
 <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>5.2.12.RELEASE</version>
 </dependency>
1
2
3
4
5
6
7
8
9
10
11

创建SpringJunitTest.java文件:

package com.muyun.test;

import com.muyun.config.SpringConfiguration;
import com.muyun.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.sql.SQLException;

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
    @Autowired
    private UserService userService;

    @Autowired
    private DataSource dataSource;

    @Test
    public void test1() throws SQLException {
        userService.save();
        System.out.println(dataSource.getConnection());
    }
}
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