dbutils+Spring完成单表的crud
准备
1、创建的数据库表。
CREATE TABLE s_account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
money FLOAT
)CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO s_account(NAME,money) VALUES('aaa',1000);
INSERT INTO s_account(NAME,money) VALUES('bbb',1000);
INSERT INTO s_account(NAME,money) VALUES('ccc',1000);
2、新建Maven工程,Jar包形式,在pom文件中导入坐标。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.coydone</groupId>
<artifactId>spring02_ioc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
3、编写实体类
//账户的实体类
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
//省略有参、无参构造,getter()和setter()方法,toString()方法
}
4、编写dao接口即其实现类
//账户的持久层接口
public interface AccountDao {
// 查询所有
List<Account> findAll();
//根据id查询
Account findAccountById(Integer id);
//保存
void saveAccount(Account account);
//更新
void updateAccount(Account account);
//根据id删除
void deleteAccount(Integer id);
}
//账户的持久层实现类
public class AccountDaoImpl implements AccountDao {
private QueryRunner qr;
//定义setter()方法,便于在xml中注入QueryRunner
public void setQr(QueryRunner qr) {
this.qr = qr;
}
public List<Account> findAll() {
try {
return qr.query("select * from s_account",new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Account findAccountById(Integer id) {
try {
return qr.query("select * from s_account where id = ? ",new BeanHandler<Account>(Account.class),id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try {
qr.update("insert into s_account(name,money)values(?,?)",account.getName(),account.getMoney());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try {
qr.update("update s_account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void deleteAccount(Integer id) {
try {
qr.update("delete from s_account where id = ?",id
);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
5、编写service层接口和实现类
service接口与dao层接口一样
// 账户的业务层实现类
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
//定义setter()方法,便于xml形式注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public List<Account> findAll() {
return accountDao.findAll();
}
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer id) {
accountDao.deleteAccount(id);
}
}
XML方式配置Spring的IoC
1、在resources下bean.xml中配置Spring的IoC
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 配置service -->
<bean id="accountService" class="com.coydone.service.impl.AccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置Dao对象 -->
<bean id="accountDao" class="com.coydone.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="qr" ref="qr"></property>
</bean>
<!-- 配置QueryRunner 配置其为多例模式 -->
<bean id="qr" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 构造方法注入数据源 -->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的基本信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
<property name="user" value="root"></property>
<property name="password" value="root123"></property>
</bean>
</beans>
2、编写测试类进行单元测试
package com.coydone.test;
//使用Junit单元测试,测试我们的配置
public class AccountServiceTest {
@Test
public void testFindAll(){
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
//3.执行方法
List<Account> accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
@Test
public void testFindOne(){
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
//3.执行方法
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave(){
Account account = new Account();
account.setName("add_test");
account.setMoney(1234f);
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
//3.执行方法
as.saveAccount(account);
}
@Test
public void testUpdate(){
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
//3.执行方法
Account account = as.findAccountById(4);
account.setMoney(2345f);
as.updateAccount(account);
}
@Test
public void testDelete(){
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.得到业务层对象
AccountService as = ac.getBean("accountService",AccountService.class);
//3.执行方法
as.deleteAccount(4);
}
}
注解改进IoC案例
1、在bean.xml中,将dao和service的配置删除
<!-- 配置service -->
<bean id="accountService" class="com.coydone.service.impl.AccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置Dao对象 -->
<bean id="accountDao" class="com.coydone.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="qr" ref="qr"></property>
</bean>
2、将对象交给Spring来管理
<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">
<!-- 告知spring在创建容器时要扫描的包 -->
<context:component-scan base-package="com.coydone"></context:component-scan>
3、在dao实现类和service实现类中使用注解管理QueryRunner对象。此时不需要提供set方法。
// 账户的持久层实现类
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner qr;
}
//账户的业务层实现类
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
}
使用配置类取代XML
Spring中的新注解
@Configuration
-
作用:指定当前类是一个配置类。
-
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
@ComponentScan
-
作用:用于通过注解指定Spring在创建容器时要扫描的包。
-
属性:value,它和basePackages的作用一样,都是用于指定创建容器时要扫描的包,
-
使用此注解就等同于在xml中配置了
<context:component-scan base-package="com.coydone"></context:component-scan>
@Bean
-
作用:用于把当前方法的返回值作为bean对象存入Spring的IoC容器中。
-
属性:name:用于指定bean的id,当不写时,默认值是当前方法的名称。
-
细节:当我们使用注解配置方法时,如果方法有参数,Spring框架会去容器中查找有没有可用的bean对象。
-
查找的方式和@Autowired注解的作用是一样的。
@Import
-
作用:用于导入其它的配置类。
-
属性:value:用于指定其它配置类的字节码。
-
当我们使用Import注解之后,有Import注解的类就是父配置类,而导入的都是子配置类。
@PropertySource
-
作用:用于指定properties文件的位置。
-
属性:value:指定文件的名称和路径。
-
关键字:classpath:表示类路径下。
使用注解类改造代码
此时bean.xml中的配置还有:
<!-- 告知spring在创建容器时要扫描的包 -->
<context:component-scan base-package="com.coydone"></context:component-scan>
<!-- 配置QueryRunner -->
<bean id="qr" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 注入数据源 -->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的基本信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
<property name="user" value="root"></property>
<property name="password" value="root123"></property>
</bean>
通过配置类的形式取代:
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
//该类是一个配置类,和bean.xml的作用一样
//主配置类
@Configuration
@ComponentScan("com.coydone")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
//和spring连接数据库相关的配置类
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
//用于创建一个QueryRunner对象
@Bean(name="runner")
@Scope("prototype")
public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
return new QueryRunner(dataSource);
}
//创建数据源对象
@Bean(name="ds1")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
在resources下创建jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis
jdbc.username=root
jdbc.password=root123
我们可以删除bean.xml文件进行测试,测试方法中的通过xml方式获取容器要改成通过注解方式获取容器。
//1.获取容器
//ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
Spring整合Junit改进测试
应用程序的入口是main方法。Junit单元测试中,没有main方法也能执行,因为junit集成了一个main方法,该方法就会判断当前测试类中哪些方法有@Test注解,Junit就会让有Test注解的方法执行。
按照以前的方法添加一个@Before注解。可以处理冗余的代码。
public class AccountServiceTest {
private ApplicationContext ac;
private AccountService as;
@Before
public void init(){
//1.获取容器
ac = new ClassPathXmlApplicationContext("bean.xml");
//2.得到业务层对象
as = ac.getBean("accountService",AccountService.class);
}
@Test
public void testFindAll(){
//3.执行方法
List<Account> accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
}
而如果是测试工程师,那么我们Before中的代码只能由我们开发人员编写,测试人员可能不会。上面的例子,我们想使用Spring的注解。
而Junit不会管我们是否使用Spring框架,在执行测试方法时,Junit根本不知道我们是不是使用了Spring框架,所以也不会为我们读取配置文件/配置类创建Spring核心容器。
所以,当测试方法执行时,没有IoC容器,就算写了Autowired注解,也无法实现注入。
@Autowired
private AccountService as;
我们需要使用Spring整合Junit的依赖进行测试方法的改造。
1、导入Spring整合Junit的依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2、使用Junit提供的Runwith注解把原有的main方法替换成Spring提供的。
3、告知Spring的运行器,Spring的IoC创建是基于xml还是注解的,并且说明位置。使用注解ContextConfiguration。属性有:
-
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下。
-
classes:指定注解类所在的位置。
注意:当我们使用spring5.x版本的时候,要求Junit的Jar必须是4.12及以上。
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
@Autowired
private AccountService as;
@Test
public void testFindAll(){
//3.执行方法
List<Account> accounts = as.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
}
评论区