Spring整合JDBC

Spring+JDBC组合开发简介

在企业级的应用中会频繁跟数据库打交道,Spring在JDBC基础上做了封装,在数据库连接获取方面引入的数据库连接池,提供了数据库操作模板类JdbcTemplate类,极大简化的数据库操作API,提高数据库操作的运行性能,同时结合Spring的AOP特性提供了一套简便的数据库事务管理机制,可以通过注解对事务进行管理。主要解决事务的一致性。

编程式事务和声明式事务的区别

编程式事务:能够精确到语句上,作用范围小。
声明时事务:声明式事务作用在方法上,作用范围大,精确小。

所需要的依赖

  <dependencies>
        <!-- Spring核心包,Spring的框架包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.5</version>
        </dependency>

        <!-- AOP切面包,用来拦截方法和注入通知 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <!-- 数据库连接池,阿里的数据库连接池,默认有十个数据库连接对象 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.5</version>
        </dependency>

        <!--Oracle JDBC 驱动包(将自己的jdbc驱动导入maven仓库)-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>1.0</version>
        </dependency>

        <!--JDBC、Tx、关系对象映射包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.5</version>
        </dependency>

    </dependencies>

将自带的jar包导入maven仓库

在命令行输入:
mvn install:install-file -Dfile="E:\jsd1906\03_JDBC\ojdbc14.jar" -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=1.0 -Dpackaging=jar

-Dfile:jar所在的文件路径
-DgroupId:引入坐标的groupId名称
-DartifactId:引入坐标的artifactId名称
-Dversion:引入坐标的version名称
-Dpackaging:生成文件类型

数据源的配置

properties文件

在类路径下创建文件db.properties,在文件中加入数据库连接信息及连接池配置信息,为spring容器创建数据库连接池提供信息,配置文件内容如下:
#数据库驱动
driverClassName=oracle.jdbc.driver.OracleDriver
#连接Url
url=jdbc:oracle:thin:@127.0.0.1:1521:XE
#用户名
username=test
#密码
password=test
#连接池启动时初始连接数 
initialSize=2
#最大活动连接数
maxActive=4
# 最大空闲值.当经过一个高峰后, 可以将已经用不到的连接慢慢释放一部分,减少到maxIdle为止
maxIdle=3
#最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请一些连接,以免洪峰来时来不及申请 
minIdle=2

xml文件

<!-- 加载jdbc.properties配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
 <!-- 配置数据源需要commons-dbcp.jar和commons-pool.jar- -->
 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${driverClassName}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
 <!-- 连接池启动时的初始值 -->
 <property name="initialSize" value="${initialSize}"/>
 <!-- 连接池的最大值 -->
 <property name="maxActive" value="${maxActive}"/>
 <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
 <property name="maxIdle" value="${maxIdle}"/>
 <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
 <property name="minIdle" value="${minIdle}"/>
  </bean>

Spring+JDBC组合开发-基于XML方式配置事务

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--包扫描-->
    <context:component-scan base-package="com.entor.jdbc"/>

    <!--
    配置数据源
    负责初始化数据库连接池
    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
        <property name="username" value="jsd2101"/>
        <property name="password" value="jsd2101"/>
    </bean>

    <!--
    配置Spring数据源事务管理器
    用来管理数据库连接池内的事务的提交和回滚
    -->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--
    配置事务传播特性,设置目标对象的那些方法中使用事务管理
    设置事务管理者
    方法名以add,save,del,update,modify,remove开头的需要事务管理。
    除此以外的不需要事务管理,只读操作。
    不需要前置通知、后置通知等,事务默认封装了一系列的通知在内,若要自定义通知可以另外定义切面注入。
    -->
    <tx:advice id="advice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="add*" propagation="REQUIRED"/>
            <!--回滚原则 默认发生运行异常时回滚  自定义时,rollback-for优先-->
            <tx:method name="del*" propagation="REQUIRED" rollback-for="Exception" no-rollback-for="RunTimeException"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="modify*" propagation="REQUIRED"/>
            <tx:method name="remove*" propagation="REQUIRED"/>
            <tx:method name="*" propagation="NOT_SUPPORTED" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!--配置aop管理切入点和通知-->
    <aop:config>
        <!--定义切面,匹配所有的连接点方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.entor.jdbc.service.impl.*.*(..))"/>
        <!--把切面和通知关联上-->
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

        <!--设置数据库操作对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务(开启事务的方法绝大部分使用这种类型事务)。

NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。

REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。

MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。

SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。

NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效

使用

Dao层

@Repository
public class StudentDaoImpl implements StudentDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    //新增
    public void add(Student student) {
        String sql = "insert into student(id,name,username,password,sex,age,birthday,create_time)values (seq_stu.nextval,?,?,?,?,?,?,sysdate)";
        Object[] args = {student.getName(), student.getUsername(), student.getPassword(), student.getSex(), student.getAge(), student.getBirthday()};
        int i = jdbcTemplate.update(sql, args);
        System.out.println("更新了" + i + "条数据");
    }

    //批量新增
    public void addMore(List<Student> list) {
        String sql = "insert into student(id,name,username,password,sex,age,birthday,create_time)values (seq_stu.nextval,?,?,?,?,?,?,sysdate)";
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        for (Student student : list) {
            Object[] args = {student.getName(), student.getUsername(), student.getPassword(), student.getSex(), student.getAge(), student.getBirthday()};
            batchArgs.add(args);
        }
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    }

    //删除
    public void deleteById(Integer id) {
        int i = jdbcTemplate.update("delete student where  id = " + id);
        System.out.println("删除了" + i + "条数据");
    }

    //批量删除
    public void deleteByIds(String ids) {
        int i = jdbcTemplate.update("delete student where  id in " + ids);
        System.out.println("删除了" + i + "条数据");
    }

    public void update(Student student) {
        String sql = "update student set name=?,username=?,password=?,sex=?,age=?,birthday=? where id=?";
        Object[] args = {student.getName(), student.getUsername(), student.getPassword(), student.getSex(), student.getAge(), student.getBirthday(), student.getId()};
        int i = jdbcTemplate.update(sql, args);
        System.out.println("更新了" + i + "条数据");
    }

    //查找
    public Student queryById(Integer id) {
        return jdbcTemplate.queryForObject("select * from student where id=" + id, (rs, i) -> {
            Student student = new Student();
            student.setId(rs.getInt("id"));
            student.setName(rs.getString("name"));
            student.setUsername(rs.getString("username"));
            student.setPassword(rs.getString("password"));
            student.setSex(rs.getInt("sex"));
            student.setAge(rs.getInt("age"));
            student.setBirthday(rs.getDate("birthday"));
            student.setCreateTime(rs.getTimestamp("create_Time"));
            return student;
        });
    }

    //分页查找
    public List<Student> queryByPage(Integer page, Integer size) {
        Object[] args = {page * size, (page - 1) * size};

        return jdbcTemplate.query("select * from (select s.*,rownum r from (select * from student)s where rownum<=?)ss where ss.r>?", (rs, i) -> {
            Student student = new Student();
            student.setId(rs.getInt("id"));
            student.setName(rs.getString("name"));
            student.setUsername(rs.getString("username"));
            student.setPassword(rs.getString("password"));
            student.setSex(rs.getInt("sex"));
            student.setAge(rs.getInt("age"));
            student.setBirthday(rs.getDate("birthday"));
            student.setCreateTime(rs.getTimestamp("create_Time"));
            return student;
        }, args);
    }

    //总记录数
    public int getTotal() {
        return jdbcTemplate.queryForObject("select count(*) from student", int.class);
    }

    //登录验证
    public Student login(String username, String password) {
        Object[] args = {username, password};

        return jdbcTemplate.queryForObject("select * from student where username=? and password=?", (rs, i) -> {
            Student student = new Student();
            student.setId(rs.getInt("id"));
            student.setName(rs.getString("name"));
            student.setUsername(rs.getString("username"));
            student.setPassword(rs.getString("password"));
            student.setSex(rs.getInt("sex"));
            student.setAge(rs.getInt("age"));
            student.setBirthday(rs.getDate("birthday"));
            student.setCreateTime(rs.getTimestamp("create_Time"));
            return student;
        }, args);
    }

    
    @Override
    public StudentClass getStudentClass(Integer id) {
        Object[] args = {id};
        return jdbcTemplate.queryForObject("select * from student_class sc where  sc.class_no = (select class_no from student where id  = ?)", (rs, i) -> {
            StudentClass studentClass = new StudentClass();
            studentClass.setClass_no(rs.getInt("class_no"));
            studentClass.setClass_name(rs.getString("class_name"));
            return studentClass;
        }, args);
    }

    @Override
    public List<Student> queryByClassNo(String name) {
        Object[]args={name};
        return jdbcTemplate.query("select * from student where class_no = (select class_no from Student_Class where class_name=?)",(rs,i)->{
            Student student = new Student();
            student.setId(rs.getInt("id"));
            student.setName(rs.getString("name"));
            student.setUsername(rs.getString("username"));
            student.setPassword(rs.getString("password"));
            student.setSex(rs.getInt("sex"));
            student.setAge(rs.getInt("age"));
            student.setBirthday(rs.getDate("birthday"));
            student.setCreateTime(rs.getTimestamp("create_Time"));
            return student;
        },args);
    }
}

Test

public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-jdbc.xml");
        StudentService service = context.getBean(StudentService.class);

        //新增
/*        Student student = new Student();
        student.setName("小黑");
        student.setUsername("1008666");
        student.setPassword("9998888");
        student.setSex(0);
        student.setAge(26);
        student.setBirthday(new Date());
        service.add(student);*/
}

解析:把方法变为一个事务,方法出现异常时事务回滚。

@Transactional注解与配置文件的使用

xml文件,去掉了通知和切入点的声明和关联。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--包扫描-->
    <context:component-scan base-package="com.entor.jdbc"/>

    <!--
    配置数据源
    负责初始化数据库连接池
    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
        <property name="username" value="jsd2101"/>
        <property name="password" value="jsd2101"/>
    </bean>
    <!--
    配置Spring数据源事务管理器
    用来管理数据库连接池内的事务的提交和回滚
    -->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--
    配置事务传播特性,设置目标对象的那些方法中使用事务管理
    设置事务管理者
    方法名以add,save,del,update,modify,remove开头的需要事务管理。
    除此以外的不需要事务管理,只读操作。
    -->
    <!--使用注解驱动把事务添加在目标对象的方法中,目标对象方法需要使用事务则在目标对象上添加注解@Transactional-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
        <!--设置数据库操作对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

Dao层

    //删除,该方法开启事务
    @Transactional
    public void deleteById(Integer id) {
        int i = jdbcTemplate.update("delete student where  id = " + id);
        System.out.println("删除了" + i + "条数据");
    }

放在类上则默认所有方法开启事务。

@EnableTransactionManagement的使用(去掉配置配置文件)

properties文件

jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:XE
jdbc.username=jsd2101
jdbc.password=jsd2101

配置类

@Configuration
@PropertySource(value = "db.properties")//加载类路径下的资源配置文件文件,结合@Value注解使用
@ComponentScan(value = "com.entor.jdbc")//组件包扫描,扫描指定包以及子包,加载该包下的所有注解。
@EnableTransactionManagement//开启事务管理,等价于配置文件的<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
public class SpringConfig {

    @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
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public TransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }

}

Dao层

    //删除,该方法开启事务
    @Transactional
    public void deleteById(Integer id) {
        int i = jdbcTemplate.update("delete student where  id = " + id);
        System.out.println("删除了" + i + "条数据");
    }

Test

//开启组件包扫描后,直接访问该类即可,其他注解的功能交给组件包扫描。
public class Test2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(StudentServiceImpl.class);
        StudentServiceImpl studentService = context.getBean(StudentServiceImpl.class);
        studentService.queryById(10);
    }
}

补充@ImportResource(location=“classpath:”***.xml")

导入配置文件,相当于在一个配置文件中导入另一个配置文件

Spring Bean的生命周期注入

单个实体类对象注入
实体类中实现接口:InitializingBean,DisposableBean
方法:

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("User对象初始化之前");
    }
    
    @Override
    public void destroy(){
        System.out.println("User对象初始化之后");
    }

所有实体类对象注入:
在配置类中实现接口:InstantiationAwareBeanPostProcessor
实例:

@Configuration
public class Confi implements InstantiationAwareBeanPostProcessor {

    @Bean(initMethod = "init",destroyMethod = "destroy")
    public User user(){
        return new User(1,"张三",20);
    }


    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("所有对象实例化之前");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("所有对象实例化之后");
        return true;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("所有对象初始化之前");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("所有对象初始化之后");
        return null;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值