DAO

学习dao前有数据库基础,java与数据库连接使用的是jdbc,在此前我已学习过jdbc也不再介绍jdbc,直接使用Spring的DAO模块,而在使用Spring的DAO,最基础的DAO模块对JDBC的支持。依赖于commons-dbcp.jar和commons-pool.jar。在pox中新添依赖。

<!--    jdbc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.2.6.RELEASE</version>
    </dependency>
<!--    连接池dbcp依赖pool-->
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>commons-pool</groupId>
      <artifactId>commons-pool</artifactId>
      <version>1.6</version>
    </dependency>
<!--    mysql-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>

在传统的JDBC中需要创建连接、打开、执行SQL、关闭连接一系列的步骤,Spring对JDBC进行了封装,我们只需使用封装好的JdbcTemplate执行SQL语句就可以了。通过JdbcDaoSupport获取JdbcTemplate对象。

DAO的学习使用mysql,通过下语句新建一个数据库并插入5个值。

USE test;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `money` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=gbk;

INSERT INTO `t_user` VALUES ('1', '张三', '24', '666.66');
INSERT INTO `t_user` VALUES ('2', '李四', '25', '888.88');
INSERT INTO `t_user` VALUES ('3', '王二', '26', '999.99');
INSERT INTO `t_user` VALUES ('4', '小明', '27', '555.55');
INSERT INTO `t_user` VALUES ('5', '小赵', '28', '333.33');

一、JdbcDaoSupport的使用

该例实现了对上述创建的数据库进行输出和插入的功能。

1、User类

新建User类,定义类字段与数据库字段对应,并声明构造函数。

public class User {
    @Override
    public String toString(){
        return "Id:"+this.getId()+" Name:"+this.getName()
                +" Age:"+this.getAge()+" Money:"+this.getMoney();
    }
    private int Id;
    private String Name;
    private int Age;
    private double Money;
    public User(){}
    public User(String name,int age,double money){
        Name = name;
        Age = age;
        Money = money;
    }
    public int getId() {
        return Id;
    }
    public void setId(int id) {
        Id = id;
    }
    public String getName() {
        return Name;
    }
    public void setName(String name) {
        Name = name;
    }
    public int getAge() {
        return Age;
    }
    public void setAge(int age) {
        Age = age;
    }
    public double getMoney() {
        return Money;
    }
    public void setMoney(double money) {
        Money = money;
    }
}

2、IUserDAO接口

QueryAllUser方法查询数据库所有信息,AddUser为插入信息操作。

public interface IUserDAO {
    public List<User> QueryAllUser();
    public Boolean AddUser(User user);
}

3、Xml配置

配置了数据库源,数据库用户和密码,因为SpringDAODemo中继承了JdbcDaoSupport,所以要设置dapends-on,这样才能从数据源中操作。

<?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">
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://127.0.0.1:3306/test</value>
        </property>
        <property name="username">
            <value>root</value>
        </property>
        <property name="password">
            <value>951753</value>
        </property>
    </bean>
    <bean id="userDao" class="test.DAO.SpringDAODemo" depends-on="dataSource">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

4、SpringDAODemo

程序入口类,并继承JdbcDaoSupport和实现IUserDAO接口中的方法。从JdbcDaoSupport中获取JdbcTemplate对象,使用queryForList方法进行sql查询,使用update方法进行sql更新。

在获取查询结果时,是将查询结果中的各行放到一个Map中,再将各个Map放到List中。因此得到结果后还需转化成List

public class SpringDAODemo extends JdbcDaoSupport implements IUserDAO {
    public static void main (String [] args){
        ApplicationContext context = 
		new ClassPathXmlApplicationContext("DAO/app.xml");
        IUserDAO userDAO = (IUserDAO)context.getBean("userDao");
        User user = new User("JdbcDaoSupportTest",26,333.33);
        userDAO.AddUser(user);
        List<User> list = userDAO.QueryAllUser();
        for(User u:list){
            System.out.println(u.toString());
        }
    }
    @Override
    public List<User> QueryAllUser() {
        String sql = "select id,name,age,money from t_user order by id desc";
        List<Map<String,Object>> list = getJdbcTemplate().queryForList(sql);
        List<User> userList = new ArrayList<>();
        for(Map<String,Object> row:list){
            User user  = new User();
            user.setId((Integer)row.get("id"));
            user.setName((String)row.get("name"));
            user.setAge((Integer)row.get("age"));
            user.setMoney(Double.parseDouble(row.get("money").toString()));
            userList.add(user);
        }
        return userList;
    }
    @Override
    public Boolean AddUser(User user) {
        String sql = "insert into t_user(name,age,money) values (?,?,?)";
        int row = getJdbcTemplate().update(sql,new Object[]{user.getName(),user.getAge(),user.getMoney()});
        if(row>0){
            System.out.println("数据新增成功!");
            return true;
        }
        return false;
    }
}

运行结果

Id:1 Name:张三 Age:24 Money:666.66
Id:2 Name:李四 Age:25 Money:888.88
Id:3 Name:王二 Age:26 Money:999.99
Id:4 Name:小明 Age:27 Money:666.66
Id:5 Name:小赵 Age:28 Money:333.33
Id:6 Name:JdbcDaoSupportTest Age:26 Money:333.33

二、MappingSqlQuery的使用

刚刚的JdbcDaoSupport的使用是获取一个List<Map<String,Object>>,这样必须要写一个转换的方法,如果使用MappingSqlQuery可以返回List类型的值。

MappingSqlQuery是一个抽风象类,在使用他时需要实现它的方法mapRow。

这里相当于将JdbcDaoSupport中的Map<String,Object>,转换成了User类型,当他获取到一行数据时,会自动使用该方法,封装成User返回。

public class UserMappingSqlQuery extends MappingSqlQuery<User> {
    @Override
    protected User mapRow(ResultSet resultSet, int i) throws SQLException {
        User user = new User();
        user.setId(resultSet.getInt("id"));
        user.setName(resultSet.getString("name"));
        user.setAge(resultSet.getInt("age"));
        user.setMoney(resultSet.getDouble("money"));
        return user;
    }
}

在使用UserMappingSqlQuery时,需要传入DataSource和sql,然后执行compile编译。可以直接通过getDataSource()来获取到JdbcDaoSupport的DataSource属性。在sql语句中如果有类似预处理的?参数,可以使用setParameters()设置参数。

下面将原本SpringDAODemo.QueryAllUser()方法修改,输出表中全部信息的功能不变。返回为List<User>.

   @Override
    public List<User> QueryAllUser() {
        String sql = "select id,name,age,money from t_user where name=? or ?";
        UserMappingSqlQuery userQuery = new UserMappingSqlQuery();
        userQuery.setDataSource(getDataSource());
        userQuery.setSql(sql);
        userQuery.setParameters(new SqlParameter[]{
                new SqlParameter(Types.VARCHAR)
                ,new SqlParameter(Types.VARCHAR)
        });
        userQuery.compile();
        return userQuery.execute(new Object[]{"1","1=1"});
    }

将方法替换后,执行结果与使用JdbcDaoSupport结果一致。

三、SqlUpdate的使用

SqlUpdate主要用来更新,将某个功能模块化。下例中,通过构造函数设置必须的参数,再使用updateUserMoney调用继承的update函数来更新数据。

public class UserSqlUpdate extends SqlUpdate {
    public static void main(String [] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("DAO/app.xml");
        UserSqlUpdate userSqlUpdate = (UserSqlUpdate)context.getBean("userSqlUpdate");
        userSqlUpdate.updateUserMoney("小明",666.66);
    }
    public UserSqlUpdate(DataSource ds){
        setDataSource(ds);
        setSql("update t_user set money=? where name=?");
        declareParameter(new SqlParameter(Types.DOUBLE));
        declareParameter(new SqlParameter(Types.VARCHAR));
        compile();
    }
    public Boolean updateUserMoney(String name,double money){
        int row = update(new Object[]{new Double(money), name});
        if(row>0){
            System.out.println("money更改成功");
            return true;
        }
        return false;
    }
}

xml配置中为userSqlUpdata配置。

    <bean id="userSqlUpdate" class="test.DAO.UserSqlUpdate">
        <constructor-arg ref="dataSource" index="0"/>
    </bean>

四、Spring事务管理

Spring中的事务管理是将DataSourceTransactionManager作为JDBC的事务管理者,同时把被管理的对象使用TransactionProxyFactoryBean配置,使用的也是代理模式。

用转账来示例事务管理,在IUserDAO接口中增加转账方法transfer

public Boolean transfer(int fromUserId,int toUserId, float transferMoney);

在SpringDAODemo类中实现transfer,从fromUserId中扣钱增加到toUserId中。其中有一个人为的制作的错误int i = 1/0被注释。

   @Override
    public Boolean transfer(int fromUserId, int toUserId, float transferMoney) {
        Boolean out = outInMoney(fromUserId,-transferMoney);
//        int i = 1/0;
        Boolean in = outInMoney(toUserId,transferMoney);
        return out&in;
    }
    private Boolean outInMoney(int toUserId,float money){
        String sql = "update t_user set money=money+? where id=?";
        int row = getJdbcTemplate().update(sql,new Object[]{money,toUserId});
        if(row>0){
            return true;
        }
        return false;
    }

xml中配置管理事务,代理模式的注入我们已经很熟悉,这里是创建一个事务管理者,然后将事务管理者和被管理的对象注入到TransactionProxyFactoryBean中生成代理对象。在xml中增加以下代码。

<bean id="transactionManager" class=
   "org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!--    配置业务层代理-->
    <bean id="userDaoProxy" class="org.springframework.transaction
		.interceptor.TransactionProxyFactoryBean">
<!--        配置目标对象-->
        <property name="target" ref="userDao"/>
<!--        注入事务管理器-->
        <property name="transactionManager" ref="transactionManager"/>
<!--        注入事务的属性-->
        <property name="transactionAttributes">
            <props>
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>

注意事务属性PROPAGATION_REQUIRED的意义。

假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务。

因为扣钱和加钱都在同一个方法中,这时两个事务实际在同一个事务中。若在方法transfer第一次事务发生前,并无事务,在扣钱时,这时事务已经产生,并在第二次操作进行加钱时进行事务判断,已有事务并在同一个事务中,这时将第二次操作添加到已有事务。方法结束该事务结束。当在该事务中发生任何异常,将回滚已在该事务中的任何操作。

main

public static void main (String [] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("DAO/app.xml");
        IUserDAO userDAO = (IUserDAO)context.getBean("userDaoProxy");
        try{
            userDAO.transfer(1,2,100);
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("事务回滚");
        }
    }

成功将张三的钱转账给李四。当发生事务回滚时,转账会被回滚

Id:6 Name:JdbcDaoSupportTest Age:26 Money:333.33
Id:5 Name:小赵 Age:28 Money:333.33
Id:4 Name:小明 Age:27 Money:666.66
Id:3 Name:王二 Age:26 Money:999.99
Id:2 Name:李四 Age:25 Money:988.88
Id:1 Name:张三 Age:24 Money:566.66