Beginning Spring学习笔记——第4章(二)使用Spring执行数据访问操作

使用JdbcTemplate运行查询


常用query(), queryForObject(), queryForList(), queryForMap(), queryForRowSet()以及它们的重载版本进行查询
下queryForObject()实现AccountDaoJdbcImpl中的find(long accountId)方法

public Account find(long accountId) {
        return jdbcTemplate
                .queryForObject(
                        "select id,owner_name,balance,access_time,locked from account where id = ?",
                        new RowMapper<Account>() {
                            public Account mapRow(ResultSet rs, int rowNum)
                                    throws SQLException {
                                Account account = new Account();
                                account.setId(rs.getLong("id"));
                                account.setOwnerName(rs.getString("owner_name"));
                                account.setBalance(rs.getDouble("balance"));
                                account.setAccessTime(rs
                                        .getTimestamp("access_time"));
                                account.setLocked(rs.getBoolean("locked"));
                                return account;
                            }

                        }, accountId);
    }

然后使用如下SQL语句插入一个账户

INSERT INTO ACCOUNT (ID, OWNER_NAME, BALANCE, ACCESS_TIME, LOCKED) VALUES (100, 'Van Gogh', 10.0 , '2017-08-29', false)

再在Main函数中使用accountDao Bean获取账户记录

public class Main {
    public static void main(String[] args) throws SQLException {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Ch4Configuration.class);

        AccountDao accountDao = applicationContext.getBean(AccountDao.class);

        Account account = accountDao.find(100L);

        System.out.println(account.getId());
        System.out.println(account.getOwnerName());
        System.out.println(account.getBalance());
        System.out.println(account.getAccessTime());
        System.out.println(account.isLocked());
    }
}

得到结果
查询结果
相比直接使用JDBC,这种方法在模板类的回调方法中封装了所有需要的数据访问逻辑。
在查询中我们还可以使用命名参数,首先在数据访问实现类中定义命名参数,再用它实现find方法返回有相同用户名的所有账户。

public class AccountDaoJdbcImpl implements AccountDao {

    private JdbcTemplate jdbcTemplate;
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
    }
    public List<Account> find(String ownerName) {
        return namedParameterJdbcTemplate.query(
                "select id,owner_name,balance,access_time,locked from account where owner_name = :ownerName"
                , Collections.singletonMap("ownerName", ownerName), 
                new RowMapper<Account>() {
                    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                        Account account = new Account();
                        account.setId(rs.getLong("id"));
                        account.setOwnerName(rs.getString("owner_name"));
                        account.setBalance(rs.getDouble("balance"));
                        account.setAccessTime(rs.getTimestamp("access_time"));
                        account.setLocked(rs.getBoolean("locked"));
                        return account;
                    }
                });
    }

还可以使用PreparedStatement,使得多次执行相同查询的预处理步骤只执行一次从而节省时间。

public List<Account> find(final boolean locked) {
        PreparedStatementCreatorFactory psCreatorFactory = new PreparedStatementCreatorFactory(
                "select * from account where locked = ?",
                new int[] { Types.BOOLEAN });
        return jdbcTemplate.query(psCreatorFactory
                .newPreparedStatementCreator(new Object[] { locked }),
                new RowMapper<Account>() {
                    public Account mapRow(ResultSet rs, int rowNum)
                            throws SQLException {
                        Account account = new Account();
                        account.setId(rs.getLong("id"));
                        account.setOwnerName(rs.getString("owner_name"));
                        account.setBalance(rs.getDouble("balance"));
                        account.setAccessTime(rs.getTimestamp("access_time"));
                        account.setLocked(rs.getBoolean("locked"));
                        return account;
                    }

                });

    }

以上代码为获取某种锁定状态的所有账户的查找。

使用JdbcTemplate插入、更新和删除记录


通过JdbcTemplate的update()及其重载方法实现。
首先创建三个异常类表示插入、更新和删除失败,在三个函数失败时创建。

public class InsertFailedException extends DataAccessException {
    public InsertFailedException(String msg) {
        super(msg);
    }
}

public class UpdateFailedException extends DataAccessException {
    public UpdateFailedException(String msg) {
        super(msg);
    }
}

public class DeleteFailedException extends DataAccessException {
    public DeleteFailedException(String msg) {
        super(msg);
    }
}

然后创建插入函数

public void insert(Account account) {
        PreparedStatementCreatorFactory psCreatorFactory = new PreparedStatementCreatorFactory(
                "insert into account(owner_name,balance,access_time,locked) values(?,?,?,?)",
                new int[] { Types.VARCHAR, Types.DOUBLE, Types.TIMESTAMP,
                        Types.BOOLEAN });
        KeyHolder keyHolder = new GeneratedKeyHolder();
        int count = jdbcTemplate.update(
                psCreatorFactory.newPreparedStatementCreator(new Object[] {
                        account.getOwnerName(), account.getBalance(),
                        account.getAccessTime(), account.isLocked() }),
                keyHolder);
        if (count != 1)
            throw new InsertFailedException("Cannot insert account");
        account.setId(keyHolder.getKey().longValue());
    }

值得注意的是为了防止主键ID的重复,此处的ID为KeyHolder自动生成。插入操作由PreparedStatement完成。
然后创建更新函数:

public void update(Account account) {
        int count = jdbcTemplate
                .update("update account  set (owner_name,balance,access_time,locked) = (?,?,?,?) where id = ?",
                        account.getOwnerName(), account.getBalance(),
                        account.getAccessTime(), account.isLocked(),
                        account.getId());
        if (count != 1)
            throw new UpdateFailedException("Cannot update account");
    }

此处直接用JdbcTemplate完成。
最后创建删除函数:

public void delete(long accountId) {
        int count = jdbcTemplate.update("delete account where id = ?",
                accountId);
        if (count != 1)
            throw new DeleteFailedException("Cannot delete account");
    }

使用SimpleJdbcCall调用和储存过程


在Main函数中创建SimpleJdbcCall实例,指定要执行的储存过程名并声明输入输出参数,若返回ResultSet则分配名称并用RowMapper处理。然后用compile()编译过程,用execute执行。

public class Main {
    public static void main(String[] args) throws SQLException {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Ch4Configuration.class);

        JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);

        SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate);

        simpleJdbcCall
        .withProcedureName("concat")
        .withoutProcedureColumnMetaDataAccess()
        .declareParameters(
                new SqlParameter("param1", Types.VARCHAR),
                new SqlParameter("param2", Types.VARCHAR)).
                returningResultSet("result", new SingleColumnRowMapper<String>(String.class));
        simpleJdbcCall.compile();

        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("param1", "hello ");
        paramMap.put("param2", "world!");
        Map<String,Object> resultMap = simpleJdbcCall.execute(paramMap);


        List<String> resultList = (List<String>) resultMap.get("result");
        for(String value:resultList) {
            System.out.println(value);
        }

    }
}

运行得到结果,拼接了两个String实例。
结果

用batchUpdate执行批处理操作

在一个PreparedStatement中执行一系列更新操作以减少往返数据库次数,提高性能

public void update(final List<Account> accounts) {
        int[] counts = jdbcTemplate.batchUpdate(
                "update account set (owner_name,balance,access_time,locked) = (?,?,?,?) where id = ?",
                new BatchPreparedStatementSetter() {        
                        public void setValues(PreparedStatement ps, int i) throws SQLException {
                            Account account = accounts.get(i);
                            ps.setString(1, account.getOwnerName());
                            ps.setDouble(2, account.getBalance());
                            ps.setTimestamp(3, new Timestamp(account.getAccessTime().getTime()));
                            ps.setBoolean(4, account.isLocked());
                            ps.setLong(5, account.getId());
                        }

                        public int getBatchSize() {
                            return accounts.size();
                        }
                    });
                    int i = 0;
                    for(int count:counts) {
                        if(count == 0) throw new UpdateFailedException("Row not updated :" + i);
                        i++;
                    }
    }

以上代码对列表accounts内的所有用户进行了更新。

处理LOB对象


分CLOB(Character Large Object)和BLOB(Binary Large Object)两种,用于处理二进制大型数据和文本大型数据。都使用LobHandler和LobCreator接口处理,分别用来访问和设置LOB值。

final LobHandler lobHandler = new DefaultLobHandler();
        final String textualContent = "test";
        final byte[] binaryContent = textualContent.getBytes();

        final long accountId = 100L;

        jdbcTemplate
                .update("update account set (owner_photo,account_desc) = (?,?) where id = ? ",
                        new PreparedStatementSetter() {
                            public void setValues(PreparedStatement ps)
                                    throws SQLException {
                                LobCreator lobCreator = lobHandler
                                        .getLobCreator();
                                lobCreator.setBlobAsBytes(ps, 1, binaryContent);
                                lobCreator.setClobAsString(ps, 2,
                                        textualContent);
                                ps.setLong(3, accountId);
                            }
                        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在开始体验 Kafka 之前,我们需要先下载并安装 Kafka。 Kafka 的官方网站为:https://kafka.apache.org/ ,在该网站的“Downloads”页面中,我们可以找到 Kafka 的进制文件,选择合适的版本进行下载。 本篇文将以 Kafka 2.8.0 版本为例进行演示。 ## 安装 Kafka 1. 解压 Kafka 压缩包 将下载的 Kafka 压缩包解压到本地文件夹中,例如:`/usr/local/kafka_2.13-2.8.0/`。 2. 配置环境变量 将 Kafka 的 bin 目录添加到 PATH 环境变量中,以便在终端中能够直接执行 Kafka 的命令。 ```bash export PATH=/usr/local/kafka_2.13-2.8.0/bin:$PATH ``` 可以将该命令添加到 `~/.bashrc` 或 `~/.zshrc` 文件中,以便每次打开终端时自动加载。 ## 启动 Kafka Kafka 的启动需要同时启动 ZooKeeper 和 Kafka 服务。 ### 启动 ZooKeeper Kafka 使用 ZooKeeper 来存储集群的元数据和状态信息。在启动 Kafka 之前,我们需要先启动 ZooKeeper。 在终端中执行以下命令来启动 ZooKeeper: ```bash zookeeper-server-start.sh config/zookeeper.properties ``` 该命令将会默认使用 Kafka 的配置文件中的 `zookeeper.properties` 进行启动,该文件位于 Kafka 的安装目录下的 `config` 目录中。 ### 启动 Kafka 在启动 Kafka 之前,我们需要先创建一个 Kafka 主题(Topic),用于存储消息。 在终端中执行以下命令来创建一个名为 `test` 的主题: ```bash kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test ``` 该命令将会使用默认配置,在本地的 Kafka 服务中创建一个名为 `test` 的主题。 接下来,在终端中执行以下命令来启动 Kafka: ```bash kafka-server-start.sh config/server.properties ``` 该命令将会默认使用 Kafka 的配置文件中的 `server.properties` 进行启动,该文件位于 Kafka 的安装目录下的 `config` 目录中。 ### 发送和接收消息 Kafka 提供了一个命令行工具 `kafka-console-producer.sh`,用于向 Kafka 主题中发送消息。 在终端中执行以下命令来发送消息: ```bash kafka-console-producer.sh --broker-list localhost:9092 --topic test ``` 该命令将会打开一个新的终端窗口,在该窗口中输入要发送的消息,按下回车键即可发送。 Kafka 还提供了一个命令行工具 `kafka-console-consumer.sh`,用于从 Kafka 主题中接收消息。 在终端中执行以下命令来接收消息: ```bash kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning ``` 该命令将会打开一个新的终端窗口,用于显示从 Kafka 主题中接收到的消息。 在上述两个终端窗口中,可以分别输入和接收消息,进行 Kafka 的体验和测试。 ## 关闭 Kafka 在终端中执行以下命令来关闭 Kafka: ```bash kafka-server-stop.sh ``` 该命令将会停止当前正在运行的 Kafka 服务。 同样地,我们也需要关闭 ZooKeeper 服务: ```bash zookeeper-server-stop.sh ``` ## 总结 通过本篇文的演示,我们学习了如何下载、安装和启动 Kafka,并且体验了 Kafka 的基本功能,包括创建主题、发送消息和接收消息等。 在实际的生产环境中,我们需要对 Kafka 进行更加详细的配置和管理,以便保证 Kafka 的高可用性、高性能和高可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值