一、直接利用 JDBC 进行数据库编程
虽然有 Spring 和 MyBatis 等数据持久化的利器在手,但还是有必要去了解下传统的 JDBC 开发方式。无论哪种框架,其本质都是在传统的 JDBC 方式上进行了封装。
package JdbcTest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcTest {
public static void main(String[] args) {
EmployeeModel employee = null;
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://192.168.52.129:3306/employees?serverTimezone=UTC", "root", "Root123#");
ps =con.prepareStatement("select emp_no,first_name,last_name from employees where emp_no = ?");
ps.setInt(1, 10001);
rs = ps.executeQuery();
while(rs.next()) {
employee = new EmployeeModel();
employee.setEmpNo(rs.getInt(1));
employee.setFirstName(rs.getString(2));
employee.setLastName(rs.getString(3));
}
} catch (ClassNotFoundException e) {
System.out.println("class not found");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
if(!(rs == null) && !rs.isClosed()) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(!(ps == null) && ps.isClosed()) {
ps.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(con !=null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(employee);
}
}
这段代码的恼人的地方在于数据库资源的使用和销毁,这些代码被大串的 try-catch 语句包裹。Spring 则为我们进行了一次包装,将这些烦人的 try-catch 语句交给 Spring 去处理。
二、为 Spring 创建第三方数据库连接池
Spring 本身也有自己的数据类,但是过于简单,在绝大多数的项目中,我们都希望能够用数据连接池的方式去管理连接。常用的第三方连接池是 DBCP2。
在配置 DBCP2 之前,现在 properties 文件里存好数据库连接所需的信息:
database.driver = com.mysql.cj.jdbc.Driver
database.url = jdbc:mysql://192.168.52.129:3306/employees?serverTimezone=UTC
database.username = root
database.password = Root123#
database.maxtotal = 255
database.maxidle = 3
database.maxwaitmillis = 10000
maxtoal 值最大连接数,maxidle 为最大等待连接数量(超出这个数量的连接资源会被释放),maxwaitmillis 为最大等待毫秒数
然后再 spring-cfg.xml 里引入该 peoperties 文件并且建立一个连接池对象:
<bean id = "dataSource" class = "org.apache.commons.dbcp2.BasicDataSource">
<property name = "driverClassName" value = "${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
<property name="maxTotal" value="${database.maxtotal}"/>
<property name="maxIdle" value="${database.maxidle}"/>
<property name="maxWaitMillis" value="${database.maxwaitmillis}"/>
</bean>
而包装传统 jdbc 的 Spring 的类为 JdbcTemple ,我们也配置下:
<bean id="jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
<property name = "dataSource" ref = "dataSource"/>
</bean>
然后使用 JdbcTemplate 来完成上面的获取 employee 信息的功能:
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class JdbcTes2 {
public static void main(String args[]) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
JdbcTemplate jp = ctx.getBean(JdbcTemplate.class);
int id = 10001;
String sql = "select emp_no,first_name,last_name from employees where emp_no = " + id;
EmployeeModel employee = jp.queryForObject(sql, new RowMapper<EmployeeModel>() {
@Override
public EmployeeModel mapRow(ResultSet rs, int rowNum) throws SQLException {
EmployeeModel employee = new EmployeeModel();
employee.setEmpNo(rs.getInt("emp_no"));
employee.setFirstName(rs.getString("first_name"));
employee.setLastName(rs.getString("last_name"));
return employee;
}
});
System.out.println(employee);
}
}
JdbcTemplate 是 Spring 自己用来处理数据库的工具类,它很简单,甚至没有实现事务管理相关的功能。
三、MyBatis-Spring
Spring + MyBatis 是目前非常常见的组合,为此,MyBatis 也专门为 Spring 设立了新的项目,也就是 MyBatis-Spring 项目。它最大的作用就是让我们可以利用 spring 配置 SqlFactory。
先来回顾下 mybatis 的使用代码:
public static void main(String[] args) {
SqlSessionFactory sqlSessionFactory = null;
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e) {
e.printStackTrace();
}
SqlSession sqlSession = sqlSessionFactory.openSession();
Role role = (Role)sqlSession.selectOne("mybatisTest.mapper.RoleMapper.getRole",1);
System.out.println(role.getAge());
}
想要使用 MyBatis 进行数据持久化工作,有下面几个步骤
- 根据配置文件建立一个 sqlSessionFactory,在配置文件里,我们定义了数据库连接信息以及 mapper 文件的位置,MyBatis 会自动加载它们。
- 利用 sqlSessionFactory 创建一个 session。
- 进行数据操作
sqlSessionFactory 在整个项目中只需要一个就够了,我们当希望能在 Spring 里注册一个 sqlSessionFactory 的 java bean ,为了方便我们注册这个 java bean。mybatis 为我们专门提供了一个 SqlSessionFactory。它可以自己根据地址去加载 xml 文件,然后生成一个 SqlSessionFactory。
<bean id = "sqlSessionFactoryBean" class = "org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref = "dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations">
<list>
<value>classpath*:com/**/*Mapper.xml</value>
</list>
</property>
</bean>
configLocation 属性就是其配置文件的地址。
mapperLocations :我们为了使用 Mapper 需要在 mybatis-config 里去装载,利用 SqSessionFactoryBean 的 mapperLocations 属性,我们可以加载多个 mapper 文件。甚至,由于 spring 支持通配符,我们可以把所有的 mapper 用简单的正则表达式加载进来(mybatis 本身不支持通配符);
我们还可以进一步简化我们的代码。
在创建了 SqlFactory 之后,我们还需要获取 Dao 对应的实现对象,也就是代码中的:
SqlSession sqlSession = sqlSessionFactory.openSession();
Role role = (Role)sqlSession.selectOne("mybatisTest.mapper.RoleMapper.getRole",1);
能不能把这个步骤也省略了?这就需要用到另一个工具 MapperScannerConfigurer。这个类会自动扫描我们的 dao 类,然后自动为他们绑定生成的类:
它的配置如下:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.**.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<property name="annotationClass" value="org.springframework.stereotype.Repository"/>
</bean>
它会从我们指定的 basePackage 里去找 dao 类,然后利用我们制定的 sqlSessionFactoryBean 生成一个 sqlSession 然后利用这个 sqlsession 去执行我们通过 mapper 文件配置的数据库语句。其实可以理解为,它会为我们的 dao 生成对应的 daoImpl 类,并且注册成 java bean,然后我们可以通过自动注入的方式去获取这个实现类。
它的 annotationClass 表示它只会针对我们制定的注释的 dao 类进行操作,dao 类一般用 Repository 注释。
现在我们的 spring-cfg.xml 文件全部内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
<bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name = "locations">
<list>
<value>classpath:jdbc.properties</value>
<value>classpath:log4j2.properties</value>
</list>
</property>
<property name = "ignoreResourceNotFound" value = "true"/>
</bean>
<bean id = "dataSource" class = "org.apache.commons.dbcp2.BasicDataSource">
<property name = "driverClassName" value = "${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
<property name="maxTotal" value="${database.maxtotal}"/>
<property name="maxIdle" value="${database.maxidle}"/>
<property name="maxWaitMillis" value="${database.maxwaitmillis}"/>
</bean>
<bean id = "sqlSessionFactoryBean" class = "org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref = "dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations">
<list>
<value>classpath*:SpringTest/**/*Mapper.xml</value>
</list>
</property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="SpringTest.**.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<property name="annotationClass" value="org.springframework.stereotype.Repository"/>
</bean>
<bean id="jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
<property name = "dataSource" ref = "dataSource"/>
</bean>
<bean id = "performAspect" class = "SpringTest.performance.aspect.Audience"/>
<bean id = "performer" class = "SpringTest.performance.service.impl.PerformerImpl">
</bean>
</beans>
然后如何使用?
public class EmployeeTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
EmployeeDao employeeDao = ctx.getBean(EmployeeDao.class);
System.out.println(employeeDao.getEmployeeByEmpNo(10001));
}
}
通过 MyBatis-Spring 将很多重复的工作一次性在加载 IOC 容器的时候直接完成了。
需要注意,必须在 dao 类上方加上 @Repository 注释,或者去掉 MapperScannerConfigurer 里的 annotationClass 属性。
四、Spring 数据库事务管理