以下完整示例代码地址:https://gitee.com/assad/springframework-test-daohttps://gitee.com/assad/springframework-test-dao

配置使用 Spring JDBC 的基本过程

示例模块:
site/assad/domain/User(领域对象)
site/assad/dao/UserDao(DAO对象)
site/assad/applicationContext.xml(bean配置文件)
site/assad/dao/UserDaoTest(测试)

配置 Spring JDBC

一般配置Spring JDBC 的步骤如下:
(1)定义 DataSource 数据源;
(2)定义 JdbcTemplate 模板;
(3)声明一个抽象的<bean>,用于所有 DAO 复用 JdbcTemplate 属性的配置;
(4)配置具体的DAO;
示例配置文件 applicationContext.xml 如下:
<?xml version="1.0" encoding="UTF-8"?>

<beans ... >

    <!--配置数据源-->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"

        p:driverClassName="com.mysql.jdbc.Driver"

        p:url="jdbc:mysql://127.0.0.1:3306/iogames"

        p:username="root"

        p:password="mysql1994assad" />

    <!--配置JDBC模板 -->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"

        p:dataSource-ref="dataSource" />

<!--配置事务管理器,略-->
<!--配置事务增强,略-->

    <!--扫描 DAO bean-->

    <context:component-scan base-package="site.assad.dao" />

</beans>

建立 Domain 领域对象

domain 领域对象是用于对数据库中的数据表映射的对象,用于以对象操作的方式操作相应的数据表(或数据表组),即 ORM 对象映射,领域对象本身实现为一个Java Bean ;
在 Spring JDBC 这种持久化方案中,将数据表映射到领域对象,是通过在相应  DAO 对象中使用 JdbcTemplate 模板对象来进行的;
以下是示例用的领域对象,用于映射 users 表:
package site.assad.domain;

import java.io.Serializable;

import java.util.Date;

public class User implements Serializable{

    private int id;

    private String name;

    private String password;

    private Date createDate ;

    //省略 getter setter

    

}

建立 DAO 对象

以下为示例示例代码 UserDao 对象部分代码,用于对 User 领域对象进行持久化操作,即将 User 领域对象的操作映射为数据库中 users 相应的操作;
package site.assad.dao;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.core.RowCallbackHandler;

import org.springframework.stereotype.Repository;

import site.assad.domain.User;

import java.sql.ResultSet;

import java.sql.SQLException;

//将 UserDao 标注为一个 DAO Bean

@Repository

public class UserDao {

    //自动注入 JdbcTemplate bean 对象

    @Autowired

    private JdbcTemplate jdbcTemplate;

    //演示查询操作:通过 userName 入参查找数据库中的相应 User 对象

    public  User findUserByUserName(final String userName){

        //sql 模板

        String sqlStr = "SELECT user_id,user_name,user_password,create_date " +

                "FROM users where user_name = ?";

        

        //创建接受结果的 domain 对象

        final User user = new User();

        //通过 jbdcTemplate 执行 sql 查询语句,参数1:sql 模板语句,参数2:sql 模板语句中占位符的实参,参数3:对于查询结果的回调逻辑

        jdbcTemplate.query(sqlStr, new Object[]{userName}, new RowCallbackHandler() {

            @Override

            public void processRow(ResultSet rs) throws SQLException {

                user.setId(rs.getInt("user_id"));

                user.setName(rs.getString("user_name"));

                user.setPassword(rs.getString("user_password"));

                user.setCreateDate(rs.getDate("create_date"));

            }

        });

        return user;

    }

}

通过DAO对象获取领域对象

之后在代码 Servcie 层中对于数据库相关操作,是用过特定的 DAO 对象获取相应的 domain 对象,对该domain 对象进行相应操作来进行的;
比如在 Servcie 层中调用示例代码 UserDao 的 findUserByUserName() 方法:
User user = userDao.findUserByUserName("Al-assad");

System.out.println(user);

如果要在测试文件中获取自动注入的 DAO对象,可以类似如下:
package site.assad.dao;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.test.context.ContextConfiguration;

@ContextConfiguration(locations = {"classpath:site/assad/applicationContext.xml"})  //通过注解指定配置文件路径

public class UserDaoTest {

    @Autowired  //自动注入 UserDao

    private UserDao userDao;

    @Test

   ......

}

对于 Spring 配置文件的加载

(1)Java Web 项目
如果项目是一个 java web 项目,需要在项目的 web.xml 配置web监听器来启动该配置文件的全局上下文对象,并自动扫描注入bean,类似如下:
<web-app .... >

    <!--加载 Spring 配置,进行 bean 的初始化-->

    <!--指定配置文件-->

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>classpath:site/assad/applicationContext.xml</param-value>

    </context-param>

    <!--声明 web 容器-->

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

</web-app>

当然也可以如下直接通过创建 ApplicationContext 对象来手动加载相应的 DAO Bean ,但就是很不太方便,一般 Java Web 项目还是会使用以上方法来自动扫描 Bean;
(2)非 Java Web 项目
如果项目不是一个 java web 项目,可以通过 ApplicationContex 上下文来手动加载相应的 DAO Bean;(见实例代码的Test 模块):
Application ctx = new ClassPathXmlApplicationContext("site/assad/applicationContext.xml");

UserDao userDao = ctx.getBean("userDao",UserDao.class);

User user = userDao.findUserByUserName("Al-assad");

System.out.println(user);


基本的数据操作

示例模块:
site/assad/domain/User(领域对象)
site/assad/dao/UserDao(DAO对象)
site/assad/applicationContext.xml(bean配置文件)
site/assad/dao/UserDaoTest(测试)


更改数据

1)基本操作

对于更改数据操作,包括 sql 的 insert,delete,update 操作,可以使用 JdbcTemplate#update 执行这些操作,如以下DAO类的代码:
public void addUser (final User user){

        //创建 sql 模板

        String sqlStr = "INSERT INTO users(user_name,user_password,create_date,user_icon) " +

                " VALUES(?,PASSWORD(?),?,?)";

        //通过 jdbcTempalte 模板更新数据

        jdbcTemplate.update(sqlStr,new Object[]{user.getName(),user.getPassword(),user.getCreateDate(),user.getIcon()});

    }

实际上在使用 JdbcTemplate#update(String sql,Object[] params) 时候,JdbcTemplate 会根据 params 参数自动创建一个 PerparedStatementSetter 回调实例,这使用和 JdbcTemplate#update(String sql,PreparedStatementSetter pss) JdbcTemplate#update(String sql,PreparedStatmentCreator psc) 效果是一样的,一般使用时会直接使用以上示例的 update 方法,因为另外2种方法实际上没有增加额外的功能,而且会照成一定的代码冗余;

2)批量操作

JdbcTemplate 也提供了 JbdcTemplate#batchUpdate 方法用于执行批量更新操作,DAO中的示例代码如下:
 public void addUsers(final List<User> userList){

        final String sql = "INSERT INTO users(user_name,user_password,create_date,user_icon) " +

                "VALUES (?,PASSWORD(?),?,?)";

        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {

            @Override //指定该批次的记录数

            public int getBatchSize() {

                return userList.size();

            }

            @Override //绑定插入的参数

            public void setValues(PreparedStatement ps, int i) throws SQLException {

                User user = userList.get(i);

                ps.setString(1,user.getName());

                ps.setString(2,user.getPassword());

                ps.setDate(3, new java.sql.Date(user.getCreateDate().getTime()));

                ps.setString(4,user.getIcon());

            }

        });

    }

查询数据

JdbcTemplate 提供了JdbcTemplate#query 用于执行查询操作,并且提供 RowCallbackHandler ,RowMapper<T> 用于处理结果集,同时还提供 JdbcTemplate#queryForObject 和 JdbcTemplate#queryForList / JdbcTemplate#queryForMap 简便方法分别用于处理返回单值数据,多行数据;

1)处理单值查询数据

如果查询结果只是一个单值,如:“SELECT COUNT(*) FROM users” 等,此时可以使用 JdbcTempalte 提供了 JdbcTemplate#queryForObject 方法用于获取查询值;
<T> queryForObject(String sql, [Object[] args ,] Class<T> requiredType)

<T> queryForObject(String sql, [Object[] args ,] RowMapper<T> RowMapper)

以下代码块示例单值数据的查询;
//演示单值查询

//返回int值的查询

    public int getUserCount(){

        final String sql = "SELECT COUNT(*) FROM users";

        return jdbcTemplate.queryForObject(sql,Integer.class);

    }

//返回User类型的查询

    public User findUserByName(final String username){

        final String sqlStr = "SELECT user_id,user_name,user_password,create_date " +

                "FROM users where user_name = ?";

        return jdbcTemplate.queryForObject(sqlStr,new Object[]{username},User.class);

    }

2)处理单行结果

可以使用 JdbcTemplate#query 方法处理单行或多行结果,在处理单行结果时,一般使用 RowCallbackHandler 处理结果集,此时能带来必 queryForObject 方法更加灵活的代码逻辑; 
//演示查询操作(使用 RowCallbackHandler 处理结果集,单结果返回)

    public  User findUserByUserName(final String userName){

        final String sqlStr = "SELECT user_id,user_name,user_password,create_date " +

                "FROM users where user_name = ?";   //sql 模板

        final User user = new User();   //创建接受结果的 domain 对象

        //通过 jbdcTemplate 执行 sql 查询语句

        jdbcTemplate.query(sqlStr, new Object[]{userName}, new RowCallbackHandler() {

            @Override

            public void processRow(ResultSet rs) throws SQLException {

                user.setId(rs.getInt("user_id"));

                user.setName(rs.getString("user_name"));

                user.setPassword(rs.getString("user_password"));

                user.setCreateDate(rs.getDate("create_date"));

            }

        });

        return user;

    }

3)处理多行结果

一般使用中,使用 RowMapper<T> 处理多行结果集在代码上是比较方便的,如下:
//演示查询操作(使用 RowMapper<T> 处理结果集,多结果)

    public List<User> findUserByIconV2(final String icon){

        final String sql = "SELECT user_id,user_name,create_date,user_icon " +

                "FROM users " +

                "WHERE user_icon = ?";

        return jdbcTemplate.query(sql, new Object[]{icon}, new RowMapper<User>() {

            @Override

            public User mapRow(ResultSet rs, int rowNum) throws SQLException {

                User user = new User();

                user.setId(rs.getInt("user_id"));

                user.setName(rs.getString("user_name"));

                user.setCreateDate(rs.getDate("create_date"));

                user.setIcon(rs.getString("user_icon"));

                return user;

            }

        });

    }

当然也可以使用 RowCallbackHandler  处理多行结果集,如下:
//演示查询操作(使用 RowCallbackHandler 处理结果集,多结果返回)

    public List<User> findUserByIcon(final String icon){

        final String sql = "SELECT user_id,user_name,create_date,user_icon " +

                "FROM users " +

                "WHERE user_icon = ?";

        final List<User> usersList = new ArrayList<>();  //记录结果集

        jdbcTemplate.query(sql, new Object[]{icon}, new RowCallbackHandler() {

            @Override

            public void processRow(ResultSet rs) throws SQLException {

                User user = new User();

                user.setId(rs.getInt("user_id"));

                user.setName(rs.getString("user_name"));

                user.setCreateDate(rs.getDate("create_date"));

                user.setIcon(rs.getString("user_icon"));

                usersList.add(user);

            }

        });

        return usersList;

    }


※RowCallbackHandler ,RowMapper<T> 的比较
RowCallbackHandler 接口实现类是可以有状态的,无法在多个地方复用,而 RowMapper<T> 的实现类是无状态的,他的实例可以在多个地方复用;
在处理多行结果的时候,虽然 RowMapper<T> 代码冗余度更低,但是当结果集占用很大内存时,RowMapper 采用的方式是将结果集中的所有数据都放到一个 List 对象中,这样会占用大量的 JVM 内存,甚至引发 OutOfMemoryException 栈溢出异常,此时可以使用 RowCallBackHandler ,在接口方法 processRow() 中一边获取数据,一边处理完成处理,避免数据在内存中的大量堆积;

调用储存过程

JdbcTemplate 提供以下2个调用储存过程的接口方法,如下:
<T>T execute(String callString,CallableStatmentCallback<T> action)

<T>T execute(CallableStatementCreator csc,CallableStatmentCallback<T> action)   

假如mysql中有以下process
delimeter //

CREATE PROCESS get_user_count(IN in_user_age INT, OUT out_num INT)

BEGIN

    SELECT COUNT(*) INTO out_num FROM users WHERE user_age = in_user_age;

END

delimeter ;

在DAO类中调用该process代码如下:
    public int getUserCountWithAge(int age){

        String sql = "{call get_user_count(?,?)}";

        

        Integer num = jdbcTemplate.execute(sql, new CallableStatementCallback<Integer>() {

            @Override

            public Integer doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {

                cs.setInt(1,age);

                cs.registerOutParameter(2, Types.INTEGER);

                cs.execute();

                return cs.getInt(2);

            }

        });

        return num;

    }


处理特殊类型的数据

处理Date,DateTime型数据

对于数据库中的 Date,DateTime 型数据,在 Java 中常用的日期类型为 java.util.Date,同时 Java 提供了 java.sql.Date 类型用于支持数据库中的 Date,DateTime 类型,将 java.util.Date 转换为 java.sql.Date 过程如下:
//将java.util.Date 转换为 java.sql.Date

java.sql.Date sqlDate = new java.util.Date().getTime();

//将特定日期的 java.util.Date 转换为 java.sql.Date

String dateStr = "2017-12-14";

java.util.Date date = new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);

java.sql.Date sqlDate = new java.sql.Date(date.getTime());

处理LOB(BLOB/CLOB)型数据

示例代码模块:
site/assad/domain/Game(领域对象)
site/assad/dao/GameDao(DAO对象)
site/assad/applicationContext.xml(bean配置文件)
site/assad/dao/GameDaoTest(测试)


LOB大对象类型,包括 BLOB 和 CLOB 两种类型,BLOB 一般用于储存大块的二进制数据(图片数据,视频数据等),CLOB 用于储存长文本类型;
不同数据库类型中,BLOB和CLOB的实际实现类型往往不同,如:Oracle 的 BLOB/CLOB,Mysql 的 BLOB/TEXT,SQL Server 的 IMAGE/TEXT等;
JdbcTemplate 提供了数据库无关的 LobCreateor,LobHandler 接口用于用于处理 LOB 类型数据;

在对 LOB 类型数据进行处理之前,需要配置本地JDBC抽取器和对 LobHandler 或 LobCreator 进行抽取,如下:
<!--用于操作 LOB 对象的配置-->

<!--配置本地JDBC对象抽取器-->

<bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"

        lazy-init="true" />

<!--使用本地jdbc对象抽取-->

<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"

        lazy-init="true" />

其中对于不同的数据源需要使用不用的JBDC抽取器,如下:
C3P0 数据源 org.springframework.jdbc.support.nativejdbc.C3P0NativeJdbcExtractor
DBCP 数据源 org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor
JBoss 3.2.4 + 的数据源 org.springframework.jdbc.support.nativejdbc.JBossNativeJdbcExtractor
WebLogic 8.1+ 的数据源 org.springframework.jdbc.support.nativejdbc.WebLogicNativeJdbcExtractor
WebSphere 5.1+ 的数据源 org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor
ObjectWeb 的 XAPool 数据源 org.springframework.jdbc.support.nativejdbc.XaPoolNativeJdbcExtractor
如果对本地Jdbc抽取的API没有特殊要求,使用 SimpleNativeJdbcExtractor 抽取器即可;

插入 LOB 类型数据

示例代码 Dao 中插入LOB类型数据的代码如下:
    //演示对于 LOB 类型数据的插入

    public void addGame(final Game game){

        final String sql = "INSERT INTO games(game_name,game_description,game_thumbnail) " +

                "VALUES(?,?,?)";

        jdbcTemplate.execute(sql,new AbstractLobCreatingPreparedStatementCallback(this.lobHandler) {

            @Override

            protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException {

                ps.setString(1,game.getName());

                lobCreator.setClobAsString(ps,2,game.getDescription());  //设置 CLOB 字段

                lobCreator.setBlobAsBytes(ps,3,game.getThumbnail());   //设置 BLOB 字段

            }

        });

    }

相关的测试代码:
 @Test

    public void testAddGame() throws Exception{

        Resource imgRes = new ClassPathResource("tmp.jpg");

        byte[] mockImg = FileCopyUtils.copyToByteArray(imgRes.getFile());

        Game game = new Game();

        game.setName("test.io");

        game.setDescription("this is test clob text");

        game.setThumbnail(mockImg);

        gameDao.addGame(game);

    }

读取 LOB 类型数据

JdbcTemplate 默认读取 BLOB 类型的数据是以块形式读取,如下示例:
    //以块数据方式读取 LOB

    public Game getGameByName(final String gamename){

        final String sql = "SELECT game_id,game_name,game_description,game_thumbnail " +

                "FROM games " +

                "WHERE game_name = ?";

        Game game = new Game();

        jdbcTemplate.query(sql, new Object[]{gamename}, new RowCallbackHandler() {

            @Override

            public void processRow(ResultSet rs) throws SQLException {

                game.setId(rs.getInt("game_id"));

                String description = lobHandler.getClobAsString(rs,3);  //读取CLOB类型数据

                byte[] thumbnail = lobHandler.getBlobAsBytes(rs,4);   //读取BLOB类型数据

                game.setDescription(description);

                game.setThumbnail(thumbnail);

            }

        });

        return game;

    }

但是这种以块形式读取 BLOB 类型的形式,当该 BLOB 类型数据过于庞大时,会占用大量的 JVM 内存,可能会引起栈溢出,此时更适合使用流的形式读取 BLOB 数据,如下示例:
    //以流数据方式读取 LOB

    public void getGameThumbnailByName(final String gamename,final OutputStream out){

        final String sql = "SELECT game_thumbnail " +

                "FROM games " +

                "WHERE game_name = ?";

        Game game = new Game();

        jdbcTemplate.query(sql, new Object[]{gamename},

                new AbstractLobStreamingResultSetExtractor<Game>() {

                    @Override

                    protected void streamData(ResultSet rs) throws SQLException, IOException, DataAccessException {

                        InputStream in = lobHandler.getBlobAsBinaryStream(rs,1);

                        if(in != null)

                            FileCopyUtils.copy(in,out);

                    }

                });

    }

测试代码如下:
    @Test

    public void testGetThumbnailByName() throws FileNotFoundException {

        FileOutputStream fileOut = new FileOutputStream("./temp.jpg");

        gameDao.getGameThumbnailByName("test.io",fileOut);

    }

)处理单值数据

如果查询结果只是一个单值,如:“SELECT COUNT(*) FROM users” 等,此时可以使用 JdbcTempalte 提供了 JdbcTemplate#queryForObject 方法用于获取查询值;
<T> queryForObject(String sql, [Object[] args ,] Class<T> requiredType)
<T> queryForObject(String sql, [Object[] args ,] RowMapper<T> RowMapper)
以下是一个返回int值数据的查询过程;

Spring DAO(2):Spring JDBC 访问数据库相关推荐

  1. Java程序员从笨鸟到菜鸟之(七十八)细谈Spring(七)spring之JDBC访问数据库及配置详解

    利用spring访问数据库是我们ssh程序中必不可少的步骤,在没有hibernate之前,我们一般都用jdbc访问数据库,所以用jdbc访问数据库必不可少的要进行一些配置,spring中为我们提供了访 ...

  2. jsp学习 jdbc访问数据库

    因为我目前在学校有一门课程叫做javaweb,其中涉及到JavaScript中进行数据库访问的测试,经过学习发现与我们之前通过配置数据源结合spring提供的模板以及mybatis提供的方法进行数据库 ...

  3. JDBC访问数据库的基本程序

    2019独角兽企业重金招聘Python工程师标准>>> 以下是jdbc访问数据库的基本程序,今后有时间再完善下. //属性配置文件 db.properties driver = co ...

  4. android web 访问数据库,Web下的JDBC访问数据库的基本步骤

    Web下的JDBC访问数据库的基本步骤 (2012-06-02 12:09:33) 在Java程序中连接数据库的一般步骤分为一下几部分,我摘录出来,跟大家分享. (1)将数据库的JABC驱动加载到cl ...

  5. java web中Jdbc访问数据库步骤通俗解释(吃饭),与MVC的通俗解释(做饭)

    一.Jdbc访问数据库步骤通俗解释(吃饭) 1)加载驱动 Class.forName("com.microsoft.jdbc.sqlserver.SQLServer"); 2) 与 ...

  6. JDBC访问数据库步骤

    最近开始复习JAVASE,下面就看看一些之前忘记的点. JDBC访问数据库是之前自己比较迷糊的,现在正好来复习一下.数据库连接首先要有一个JDBC驱动程序,还要有一个数据库,这里两者不属于JAVA的知 ...

  7. Spring JDBC-使用Spring JDBC访问数据库

    概述 使用Spring JDBC 基本的数据操作 更改数据 返回数据库表的自增主键值 批量更改数据 查询数据 使用RowCallbackHandler处理结果集 使用RowMapperT处理结果集 R ...

  8. Spring实战6-利用Spring和JDBC访问数据库

    主要内容 定义Spring的数据访问支持 配置数据库资源 使用Spring提供的JDBC模板 写在前面:经过上一篇文章的学习,我们掌握了如何写web应用的控制器层,不过由于只定义了SpitterRep ...

  9. Spring Boot中使用JdbcTemplate访问数据库

    本文介绍在Spring Boot基础下配置数据源和通过JdbcTemplate编写数据访问的示例. 数据源配置 在我们访问数据库的时候,需要先配置一个数据源,下面分别介绍一下几种不同的数据库配置方式. ...

  10. python通过jdbc访问数据库

    python通过jdbc连接数据库 python要访问sybase或oracle数据库,尝试通过odbc以及pyodbc等方式去访问数据库,一直没有成功,通过JayDeBeApi模块调用 java j ...

最新文章

  1. SO做了Booked之后,一直处理于“已延交”,发运事务处理的活动区变灰
  2. 【工具篇】利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档(转
  3. 收藏吧!产品再要求实现这个功能,就把这篇转给他!
  4. linux mint(Ubuntu、Debian) 18修改环境变量
  5. MM 常用table
  6. 人人都可以创造自己的AI:深度学习的6大应用及3大成熟领域
  7. php sql查看本月记录,SQL Server 获取本周,本月,本年等时间内记录
  8. 如何调整html中音乐播放器的大小,html5实现在线响应式音乐播放器
  9. strtof linux内核,Qt for Android使用grpc探索
  10. 透明色的rgb值是多少_一文掌握PPT主题色原理及使用技巧
  11. java future 设计模式_转多线程设计模式 - Future模式之JAVA原生实现
  12. 初探 Linux操作系统 (一):站在巨人的肩膀上
  13. 2022.02.17学习总结(最小生成树)
  14. 一文了解各大图数据库查询语言(Gremlin vs Cypher vs nGQL)| 操作入门篇
  15. 郭彦甫Matlab第五节笔记——进阶绘图
  16. API_Application
  17. 文化中国 系列二:纪连海读历史品人物
  18. MySQL ---允许所有ip远程连接数据库
  19. 机器学习 基本概念/术语
  20. 电路知识经典汇总(邱关源)

热门文章

  1. Nginx Oracle反向代理与负载均衡配置
  2. SQL Server 数据库之事务
  3. 爬虫被锁IP,报“很抱歉,由于您访问的URL有可能对网站造成安全威胁,您的访问被阻断。 您的请求ID是······“
  4. C语言实现简单的Web服务器
  5. java在线玩具租赁系统
  6. ThinkPHP漏洞利用
  7. Spring 如何解决循环依赖问题
  8. 江西医院的智康护智慧医疗解决方案
  9. MySQL表记录检索
  10. Android中测试多国语言漏翻的情况(一)