你还在用传统的 JDBC 持久化访问吗?

本贴最后更新于 2961 天前,其中的信息可能已经时移世易

前言

这里我会采用 mybatis3.2 做数据库的持久化,很多小伙伴或许还停留在老师的教导下,仍然停留在使用传统的 JDBC 持久化访问数据层。今天,我们来聊聊面向接口编程和怎么充分利用注解的优势!

配图.jpg

实现 DAO 持久层

从目前的 Java 框架趋势来看,spring 框架仍旧占据主流,不论你使用 SSH 框架和 SSM 框架开发,必透彻 spring 是无可厚非的。

从目前互联网公司的分布来看,中小型公司仍旧拥有半壁江山,它们没有雄厚的资金和开发资源,也就是说极少有公司会使用 hibernate+struts 开发项目,对中小型公司来说开发超大型项目是不在它们的考虑范围之内的。那么,mybatis+springmvc 开发框架就此兴起...

闲聊结束,我先说说原理吧:
使用 mybatis 定义接口完成 sql 语句的映射,该接口还可以直接作为 DAO 的组件使用。

桥接模式知道吗?
桥接模式在这里的应用:
使用该模式能够体现业务逻辑组件封装 DAO 组件的模式,也可以分离业务逻辑组件和 DAO 组件的功能。也就是说,业务逻辑组件负责业务逻辑的变化,而 DAO 组件负责持久化的变化。


这样做的好处:
① 每个 DAO 组件包含了数据库的访问逻辑。
② 每个 DAO 组件可对一个数据表完成基本的 CRUD 等操作。

1.公共常量类

HrmConstants 类

/** * * 常量 * */ public class HrmConstants { //数据库表常量 public static final String USERTABLE="user_inf"; public static final String DEPTTABLE="dept_inf"; public static final String JOBTABLE="job_inf"; public static final String EMPLOYEETABLE="employee_inf"; public static final String NOTICETABLE="notice_inf"; public static final String DOCUMENTTABLE="document_inf"; //登录 public static final String LOGIN="loginFrom"; //用户的session对象 public static final String USER_SESSION="user_session"; //默认每页4条数据 public static int PAGE_DEFAULT_SIZE=4; }

这里数据库我就不贴出来了。这个公共常量类根据数据库表的结构定义得,也就是说在数据库里面我一共创建了 6 个表,每个表和此类是一一对应的。至于这里的 session 对象,是为了后期编写控制层所铺垫的,与本次所讲内容无影响噢。

2.实体类

/** * * 用户实体类 * */ public class User { private Integer id;//id private String username;//用户名 private String loginname;//登录名 private String password;//密码 private Integer status;//状态 private Date createDate;//建档日期 public User() { } //setter和getter方法 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getLoginname() { return loginname; } public void setLoginname(String loginname) { this.loginname = loginname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } }

这里,我会以用户实体类做一个案例介绍。实体类的基本准则就是遵循 javabean 规则,也就是常说的公有的类、私有的成员变量以及自带一个无参的构造方法。当然,这里我用了 setter 和 getter 方法对其进行了简单的封装。

3.定义 DAO 接口

/** * * UserDao的接口实现类 * */ public interface UserDao { //登录用户名和密码查询员工 @Select("select * from "+USERTABLE+" where loginname = #{loginname} and password = #{password} ") User selectByLoginnmameAndPassword( @Param("loginname") String loginname, @Param("password") String password ); //根据Id查询用户 @Select("select * from "+USERTABLE+" where id = #{id} ") User selectById(Integer id); //根据Id删除用户 @Delete("delete from "+USERTABLE+" where id = #{id} ") void deleteById(Integer id); //动态修改用户 @SelectProvider(method = "updateUser", type = UserDynaSqlProvider.class) void update(User user); //动态查询 @SelectProvider(method = "selectWhitParam", type = UserDynaSqlProvider.class) List<User> selectByPage(Map<String, Object> params); //根据参数查询用户总数 @SelectProvider(method = "count", type = UserDynaSqlProvider.class) Integer count(Map<String, Object> params); //动态插入用户 @SelectProvider(method = "inserUser", type = UserDynaSqlProvider.class) void save(User user); }

这里才是真正的开始升华了,首先我只定义了一个 UserDao 的接口,然后充分利用 mybatis 的注解优势,定义了'登录用户名和密码查询员工'的方法......
这里为大家补充一下注解知识:

@Select 注解,这个不用解释了吧,就相当于存放查询语句的一个注解,定义在某一个方法上,效果相当于在配置文件里面编写查询语句。

@Param 注解,是对参数的解释。这里用的 string loginname 是前面在公共常量类里面定义的。怎么说呢,我们可以理解成 request.setAttribute("","") 这种形式的升华吧。

@SelectProvider 注解,用于生成查询用的 sql 语句,有别于 @Select 注解。结构上看,@SelectProvide 指定了一个 Class 及其方法,通过调用 Class 上的这个方法来获得 sql 语句。


注意事项:
① 这里的 @Param 注解是基于 mybatis 框架的,不是 spring 框架上的,导入包的时候要注意哟!
②@SelectProvider 注解在编程的思维上有一个跨度,因为它本身需要一个 class 和其方法,所以在定义这个注解的时候,务必思考好下一个层次结构里面的类和方法所需要做的事哦!当然,在接口里面的方法,是可以事先预算好的,但是在它上面的注解,就要多思考思考才能做决定哦!
③ 公共常量类。在此接口的运用上,既然是面向接口编程,还要完成数据库的基本操作,那么是一定要导入公共常量类里面的 USERTABLE 的。
④id = #{id} 生成的 sql 语句是 id = ?

4.动态 SQL 提供类

public class UserDynaSqlProvider { //分页动态查询 public String selectWhitParam(final Map<String, Object> params) { String sql=new SQL(){ { SELECT("*"); FROM(USERTABLE); if (params.get("user")!=null) { User user=(User) params.get("user"); if (user.getUsername()!=null && !user.getUsername().equals("")) { WHERE(" username LIKE CONCAT('%',#{user.username},'%') "); } if (user.getStatus()!=null && !user.getStatus().equals("")) { WHERE(" status LIKE CONCAT('%',#{user.status},'%') "); } } } }.toString(); if (params.get("pageModel")!=null) { sql += " limit #{pageModel.firstLimitParam} , #{pageModel.pageSize} "; } return sql; } //动态查询总数量 public String count(final Map<String, Object> params) { return new SQL(){ { SELECT("*"); FROM(USERTABLE); if (params.get("user") !=null) { User user=(User) params.get("user"); if (user.getUsername()!=null && !user.getUsername().equals("")) { WHERE(" username LIKE CONCAT('%',#{user.username},'%') "); } if (user.getStatus()!=null && !user.getStatus().equals("")) { WHERE(" status LIKE CONCAT('%',#{user.status},'%') "); } } } }.toString(); } //动态插入 public String inserUser(final User user) { return new SQL(){ { INSERT_INTO(USERTABLE); if (user.getUsername()!=null && !user.getUsername().equals("")) { VALUES("username", "#{username}"); } if (user.getStatus()!=null && !user.getStatus().equals("")) { VALUES("status", "#{status}"); } if (user.getLoginname()!=null && !user.getLoginname().equals("")) { VALUES("loginname", "#{loginname}"); } if (user.getPassword()!=null && !user.getPassword().equals("")) { VALUES("password", "#{password}"); } } }.toString(); } //动态更新 public String updateUser(final User user) { return new SQL(){ { UPDATE(USERTABLE); if (user.getUsername()!=null) { SET(" username = #{username} "); } if (user.getLoginname()!=null) { SET(" loginname = #{loginname} "); } if (user.getPassword()!=null) { SET(" password = #{password} "); } if (user.getStatus()!=null) { SET(" status = #{status} "); } if (user.getCreateDate()!=null) { SET(" create_date = #{createDate} "); } WHERE(" id = #{id} "); } }.toString(); } }

正如前面编写接口类的时候所说,既然用了 @SelectProvider 注解,必然会使用到一个 class 和它的方法。我们在接口类里面用了四次 @SelectProvider 注解,那么与之对应的就应该有四个方法,分别是分页动态查询(selectWhitParam)、动态查询总数量(count)、动态插入(inserUser)、动态更新(updateUser)。

这个类是专门为接口提供 sql 语句服务的,不再需要传统的去实现接口,用注解代替了原本繁琐的事情。

ps:或许有人会疑惑,我们怎么把所写的配置在 applicationContext.xml 里面呢?会不会很麻烦?现在,我告诉你,一点也不麻烦哦!

配置文件

applicationContext.xml

<!-- mybatis:scan 会扫描com.dao.inter包下面的所有接口当作spring的bean配置,之后可以进行依赖注入 --> <mybatis:scan base-package="com.dao.inter"/> <!-- 使用PropertyOverrideConfigurer后处理器加载数据源参数 --> <context:property-override location="classpath:db.properties" /> <!-- 配置c3p0数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.CombopooledDataSource "/> <!-- 配置sqlSessionFactory。org.mybatis.spring.SqlSessionFactoryBean是mybatis社区开发用于整合Spring的bean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource"/>

db.properties

dataSource.driverClass=com.mysql.jdbc.Driver dataSource.jdbcUrl=jdbc:mysql://127.0.0.1:3306/hrm_db dataSource.user=root dataSource.password=root dataSource.maxPoolSize=20 dataSource.maxIdleTime=1000 dataSource.minPoolSize=6 dataSource.initialPoolSize=5

原理:mybatis 的持久化 DAO 接口只需要通过 SqlSession 的 getMapper 方法获得对应的接口实例,从而达到调用接口方法完成数据库的操作,而在 spring 容器里,只负责生成和管理 DAO 的组件。


看完了?那么大家觉得是不是要比传统的 JDBC 持久化访问要简单些呢?我想,至少也要方便一些吧。

  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    693 引用 • 537 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3195 引用 • 8215 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • vgm

    各有各好

  • wolf7

    在适合的场景下,使用适合的方法,方为正道。