Spring 中的JDBC框架

16 篇文章 0 订阅
12 篇文章 0 订阅

spring为我们提供了一个封装好了的数据库操作API,我们可以很方便的使用他们来完成数据库操作

以下是spring为我们做的(来自官方文档)

ActionSpringYou
Define connection parameters.X
Open the connection.X
Specify the SQL statement.X
Declare parameters and provide parameter valuesX
Prepare and execute the statement.X
Set up the loop to iterate through the results (if any).X
Do the work for each iteration.X
Process any exception.X
Handle transactions.X
Close the connection, the statement, and the resultset.X

Spring Framework负责处理所有可能使JDBC成为繁琐的API的低级细节。

连接数据库

如何连接数据库?

  • DataSource

DataSource是JDBC规范的一部分,是一个通用的连接工厂。它允许容器或框架从应用程序代码中隐藏连接池和事务管理问题。

  • DataSourceUtils

该DataSourceUtils班是一个方便且功能强大的辅助类,提供 static方法来获取从JNDI和连接,如果有必要密切的联系。例如,它支持线程绑定连接DataSourceTransactionManager。

  • SmartDataSource

该SmartDataSource接口应该由能提供一个关系数据库的连接类实现。它扩展了DataSource接口,让使用它的类查询是否应该在给定操作后关闭连接。当您知道需要重用连接时,此用法很有效。

  • AbstractDataSource

AbstractDataSource是DataSource 实现的基类。它实现了所有DataSource实现共有的代码。如果编写自己的DataSource 实现,则应该扩展AbstractDataSource类。

  • SingleConnectionDataSource

的SingleConnectionDataSource类是的一个实现SmartDataSource ,它包装单个接口Connection的是在每次使用后不关闭。这不是多线程的。
SingleConnectionDataSource主要是一个测试类。例如,它可以在简单的JNDI环境中轻松测试应用程序服务器外部的代码。与此相反 DriverManagerDataSource,它始终重用相同的连接,避免过度创建物理连接。

  • DriverManagerDataSource

该DriverManagerDataSource班是标准的实现DataSource 是通过配置bean的属性的纯JDBC驱动程序,并返回一个新的接口 Connection每次。
此实现对于Java EE容器外部的测试和独立环境非常有用,可以作为DataSourceSpring IoC容器中的bean ,也可以与简单的JNDI环境结合使用。

  • TransactionAwareDataSourceProxy

TransactionAwareDataSourceProxy是目标的代理DataSource。代理包装该目标DataSource以增加对Spring管理的事务的认知。在这方面,它类似于DataSourceJava EE服务器提供的事务性JNDI 。

  • DataSourceTransactionManager

该DataSourceTransactionManager类是PlatformTransactionManager 为单JDBC数据源的实现。它将JDBC连接从指定的数据源绑定到当前正在执行的线程,可能允许每个数据源一个线程连接。

下面是如何连接的示例代码

使用的是DriverManagerDataSource

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/school");
dataSource.setUsername("root");
dataSource.setPassword("123456");

另一种,基于xml的配置

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<!-- spring 提供的一种加载 propreties 配置文件的方式 -->
<context:property-placeholder location="classpath:jdbc.properites"/>
<!-- 或者location="src/jdbc.properites"  -->

在src下,建立jdbc.properites配置文件

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/school
jdbc.username = root
jdbc.password = 123456

一般是使用JDBCT.properites来配置,写在java代码里的方式修改时麻烦,易出错。

DriverManagerDataSource仅将该类用于测试目的,因为它不提供池,并且在进行多个连接请求时性能很差

下面是DBCP的配置

	<bean id="dataSource"
		class="org.apache.commons.dbcp2.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

可以看到,在Class里是org.apache.commons.dbcp2.BasicDataSource

我们要导入jdbc驱动包:mysql-connector-java-5.1.15-bin.jar

使用连接池,需要加入连接池的jar包,这里使用的是

  • commons-dbcp2-2.2.0.jar
  • commons-pool2-2.6.2.jar

这里要注意,使用的版本,在此版本里,org.apache.commons.dbcp2.BasicDataSource,类路径是dbcp2

下面来看一个最精简的完整示例

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">


	<bean id="studentDao" class="com.dao.StudentDao">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<bean id="dataSource"
		class="org.apache.commons.dbcp2.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<context:property-placeholder
		location="classpath:jdbc.properites" system-properties-mode="FALLBACK" />

</beans>

在配置文件里,把DataSource,注入到studentDao中

接下来,我们看StudentDao类的具体,内容

package com.dao;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.domain.Student;

public class StudentDao {
	
	private JdbcTemplate jdbcTemplate;
	
	public void setDataSource(DataSource dataSource) {
		jdbcTemplate = new JdbcTemplate(dataSource);
	}

jdbcTemplate,是spring为我们提供的数据库操作的对象,我们可以直接执行sql语句,例如:

	/**
	 * 执行sql语句,创建一个教师表
	 * 
	 */
	public void createTableTeacher() {

		String sql = "create table teacher (id integer,name varcher(20) )";
		
		jdbcTemplate.execute(sql);
	}

我们只需要提供要执行的sql语句,jdbcTemplate就会为我们完成 得到连接,执行,得到结果,关闭连接,对结果封装 然后返回,具体参考官方文档(spring-framework-reference/data-access.html#jdbc),的表格,本文的开头也有列出

数据库CRUD示例

练习数据库表

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sno` varchar(10) NOT NULL,
  `name` varchar(10) NOT NULL,
  `sex` varchar(2) NOT NULL,
  `age` int(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

对应的实体类

package com.domain;

public class Student {
	
	private int id;
	
	private String sno;
	
	private String name;
	
	private String sex;
	
	private int age;
	
    //省略set get toString方法
    //...
}

连接数据库配置文件

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=UTF-8
jdbc.username = root
jdbc.password = 123456

?useUnicode=true&characterEncoding=UTF-8显式指定数据库编码,防止乱码

StudentDao.java 数据库操作函数

package com.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.domain.Student;

public class StudentDao {
	
	private JdbcTemplate jdbcTemplate;
	
	public void setDataSource(DataSource dataSource) {
		jdbcTemplate = new JdbcTemplate(dataSource);
	}
	/**
	 * 执行sql语句,创建一个教师表
	 * 
	 */
	public void createTableTeacher() {

		String sql = "create table teacher (id integer,name varcher(20) )";
		
		jdbcTemplate.execute(sql);
        //直接执行sql语句,不需要返回值
	}
	
	
	
	/**
	 * 查询所有学生
	 * @return List<Student>学生列表
	 */
	public List<Student> listAllStudent(){
		
		String sql = "select * from student";
		
		return jdbcTemplate.query(sql, new StudentMap());
        //传入 要执行的sql语句,用StudentMap告诉spring怎么封装返回的对象
	}
	
	/**
	 * 查询所有学生2
	 */
	public List<Map<String,Object>> listAllStudent2(){
		
		String sql = "select * from student";
		
		return jdbcTemplate.queryForList(sql);
		/**
		 * 类似于 [{id=1,sno=001,name=li,....}{id=2,sno=002,name=zhang,...}]
		 * 多个map集合
		 */
	}
	
	
	/**
	 *  根据id 查找学生
	 * @return Student 学生
	 */
	public Student getStudentById(int id) {
		
		String sql = "select * from student where id=?";
		
		Student s = jdbcTemplate.queryForObject(sql,new Integer[] {id},new StudentMap());
		  //传入 要执行的sql语句,sql语句参数id(这里是一个数组),用StudentMap告诉spring怎么封装返回的对象
		return s;
	}
	
	
	/**
	 * 查询所有学生数量
	 * @return int 学生数量
	 */
	public int getAllStudentCount() {
		
		String sql = "select count(*) from student";
		
		return jdbcTemplate.queryForObject(sql, Integer.class);
        //传入 要执行的sql语句,和要求返回的类型(Integer)
		
	}
	
	/**
	 * 根据学生id查询学生的姓名
	 * @param 
	 * @return 学生姓名
	 */
	public String getStudentNameById(int id) {
		
		String sql = "select name from student where id=?";
		
		return jdbcTemplate.queryForObject(sql, String.class,id);
        //传入 要执行的sql语句,和要求返回的类型(String),查询参数
	}
	
	/**
	 * 查询所有学生的姓名
	 * @return 学生姓名的String
	 */
    public List<String> getAllStudentName() {
        return jdbcTemplate.queryForList("select name from student", String.class);
    }
	
	

    /**
     * 根据id来修改学生姓名
     * @param id
     * @param name
     * @return 受影响行数
     */ 
    public int updateStudentNameById(int id,String name) {
    	
    	String sql = "update student set name=? where id=?";
    	
    	return jdbcTemplate.update(sql, name,id);
    }
    
    /**
     *  插入一个学生
     * @param s
     * @return 受影响行数
     */
    public int insertStudent(Student s) {
    	
    	String sql = "insert into student (sno,name,sex,age) values (?,?,?,?)";
    	
    	return jdbcTemplate.update(sql, s.getSno(),s.getName(),s.getSex(),s.getAge());
    }
    
    
    
    
	/**
	 * 根据 id 删除学生表中的对应记录
	 * @param id
	 * @return
	 */
	public int deleteStudentByid(int id) {
		
		String sql = "delete from student where id=?";
		
		return jdbcTemplate.update(sql, id);
        //传入执行sql语句,和参数,返回受影响的行数
	}
	
	

    /**
    * 封装了查询对象
    */
	static class StudentMap implements RowMapper<Student>{

		public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
			Student s = new Student();
			s.setId(rs.getInt("id"));
			s.setSno(rs.getString("sno"));
			s.setName(rs.getString("name"));
			s.setSex(rs.getString("sex"));
			s.setAge(rs.getInt("age"));
			return s;
		}
		
	}	
	
}

StudentMap实现了RowMapper接口,这个接口是spring为我们提供的,专门封装查询对象的。

在getStudentById函数内(69行),Student s = jdbcTemplate.queryForObject(sql,new Integer[] {id},new StudentMap());,将StudentMap对象传入,告诉spring如何做,它就能为我们返回一个封装好的对象,当我们有大量类似查询时,可以很方便的完成目标

Beans.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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">


	<bean id="studentDao" class="com.dao.StudentDao">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<bean id="dataSource"
		class="org.apache.commons.dbcp2.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>


	<context:property-placeholder
		location="classpath:jdbc.properites" system-properties-mode="FALLBACK" />
	<!-- 或者location="src/jdbc.properites" -->

</beans>

测试TestStudentDao

package com.test;


import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.dao.StudentDao;
import com.domain.Student;

public class TestStudentDao {
	StudentDao studentDao;
	
	@Before
	public void init() {
		ApplicationContext context = new FileSystemXmlApplicationContext("src/Beans.xml");

		studentDao = (StudentDao) context.getBean("studentDao");
	}
	
	@Test
	public void TestQuery() {
		
		System.out.println("得到所有学生数量:");
		System.out.println(studentDao.getAllStudentCount());
		
		System.out.println("得到所有学生的名字");
		System.out.println(studentDao.getAllStudentName());
		
		System.out.println("得到id为1学生的所有信息");
		System.out.println(studentDao.getStudentById(1));
		
		System.out.println("得到id为1的学生姓名");
		System.out.println(studentDao.getStudentNameById(1));
		
		System.out.println("得到所有学生");
		System.out.println(studentDao.listAllStudent());
		System.out.println("另一种方式");
		System.out.println(studentDao.listAllStudent2());

	}
	
	
	@Test
	public void TestAdd() {
		
		System.out.println("增加一个学生");
		Student s = new Student();
		s.setSno("006");
		s.setName("李");
		s.setSex("男");
		s.setAge(19);
		studentDao.insertStudent(s);
		
	}
	
	
	@Test
	public void TestUpdate() {
		
		System.out.println("修改id为1的学生名字为:莉莉");
		studentDao.updateStudentNameById(1, "莉莉");
		
	}
	
	
	@Test
	public void TestDelete() {
		
		System.out.println("删除id为2的学生");
		System.out.println(studentDao.getStudentNameById(2) + "要被删除");
		
		studentDao.deleteStudentByid(2);
	}
	
}

说明:

以上的示例,只是spring为我们提供的一小部分API,详情参见官方文档

其他

NamedParameterJdbcTemplate

如果想要让代码更容易读,可以这样写

// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}

public int countOfActors(Actor exampleActor) {

    String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";

    //初始化一个解析 exampleActor所属类型(Actor),的 SqlParameterSource对象
    SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);

    return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
    //传入 要执行的sql,解析方式,要求返回的类型
}

这里使用了:firstName :last_name来做占位符,比单纯使用”?“更加清晰

  • 使用SimpleJdbc类简化JDBC操作
public class JdbcActorDao implements ActorDao {

    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcInsert insertActor;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor");
    }

    public void add(Actor actor) {
        Map<String, Object> parameters = new HashMap<String, Object>(3);
        parameters.put("id", actor.getId());
        parameters.put("first_name", actor.getFirstName());
        parameters.put("last_name", actor.getLastName());
        insertActor.execute(parameters);
    }

}
  • 批处理操作
public class JdbcActorDao implements ActorDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public int[] batchUpdate(final List<Actor> actors) {
        return this.jdbcTemplate.batchUpdate(
                "update t_actor set first_name = ?, last_name = ? where id = ?",
                new BatchPreparedStatementSetter() {
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        ps.setString(1, actors.get(i).getFirstName());
                        ps.setString(2, actors.get(i).getLastName());
                        ps.setLong(3, actors.get(i).getId().longValue());
                    }
                    public int getBatchSize() {
                        return actors.size();
                    }
                });
    }

    // ... additional methods
}
  • 嵌入式数据库支持

…等等,具体看官方文档

关于加载propreties配置文件的一个小坑

<!-- 这是改完之后的正确写法 -->
<bean id="dataSource"
		class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> 
		<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> -->
		<property name="driverClassName" value="${jdbc.driver}"/>
		<property name="url" value="${jdbc.url}"/>
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean> 
	
	<!-- 加载配置 -->
	<context:property-placeholder location="classpath:jdbc.properites"/>

使用spring提供的类加载 jdbc.properites 时,发现<property name="username" value="${username}"里面的${username}取值不对,报错 说Adminstor @ 'localhost'密码错误。

原因是,这里使用了EL表达式,${username},代表的是,当前系统用户名(我的是win7-》取到的是Adminstor)

解决方式是

  • 设置属性

<context:property-placeholder location=“classpath:jdbc.properites” system-properties-mode=“FALLBACK”/>

FALLBACK  — 默认值,不存在时覆盖
NEVER    — 不覆盖

  • 避开

当然,最好是不要这么写,给属性加一个前缀避开就好

<property name="username" value="${jdbc.username}"/>

数据库连接要指定编码,否则会出现乱码

参考链接

https://www.w3cschool.cn/wkspring/iuck1mma.html

https://blog.csdn.net/fdk2zhang/article/details/83039416

https://blog.csdn.net/lxh123456789asd/article/details/81042826

官方文档

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值