mybatis之注解开发
文章目录
mybatis注解开发
环境搭建
新建maven工程
先看下完整的测试工程结构
工程结构说明
- dao包:和数据库交互的接口(增删改查)
- entity包:实体定义
- jdbcConfig.properties:jdbc连接的配置文件
- SqlMapperConfig.xml:mybatis的配置文件
pom.xml配置
<?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>org.example</groupId>
<artifactId>mybatis_annotaion</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 打包方式 -->
<packaging>jar</packaging>
<!-- 所有依赖-->
<dependencies>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 调试工具依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
mybatsi配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 配置文件的根元素 -->
<configuration>
<!-- 引入jdbc配置文件 -->
<properties resource="com/jdbcConfig.properties"></properties>
<!-- 环境:配置mybatis的环境 -->
<environments default="mysql">
<!-- 环境变量:可以配置多个环境变量,比如使用多数据源时,就需要配置多个环境变量 -->
<environment id="mysql">
<!-- 事务管理器 -->
<transactionManager type="jdbc"></transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<!--
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.1.8:3306/studenms?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="sa"/>
-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.dao"/>
</mappers>
</configuration>
这里采用引入jdbc配置文件,也可直接配置,即dataSource节点中注释部分。和dao接口的映射,由于是采用注解开发,因此用package映射。jdbc的配置文件jdbcConfig.properties内如下:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.1.8:3306/studenms?serverTimezone=UTC
jdbc.username=root
jdbc.password=sa
注意等号后边的字符串不要加双引号,加了双引号之后,在解析配置时候,会将双引号也解析出来,导致无法连接jdbc
查询实现
数据表说明
- t_user 用户表
- t_score 分数表
实体定义
用户信息实体
package com.entity;
import java.util.List;
public class UserEntity {
private int id;
private String user;
private int role;
private String name;
private String email;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "UserEntity{" +
"id=" + id +
", user='" + user + '\'' +
", role=" + role +
", name='" + name + '\'' +
", email='" + email + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
分数实体
package com.entity;
public class ScoreEntity {
private int uId;
private int id;
private int scores;
public int getuId() {
return uId;
}
public void setuId(int uId) {
this.uId = uId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getScores() {
return scores;
}
public void setScores(int scores) {
this.scores = scores;
}
@Override
public String toString() {
return "Score{" +
"uId=" + uId +
", id=" + id +
", scores=" + scores +
'}';
}
}
dao接口
package com.dao;
import com.entity.UserEntity;
import java.util.List;
public interface IUserDao {
@Select("select * from t_user")
List<UserEntity> findAll();
@Select("select * from t_user where id=#{id}")
UserEntity findById(Integer id);
}
package com.dao;
import com.entity.ScoreEntity;
import java.util.List;
public interface IScoreDao {
@Select("select * from t_score")
List<ScoreEntity> findAll();
@Select("select * from t_score where id=#{id}")
ScoreEntity findById();
@Select("select * from t_score where id=#{id}")
List<ScoreEntity> findByUserId(Integer userId);
}
测试程序
import com.dao.IScoreDao;
import com.dao.IUserDao;
import com.entity.ScoreEntity;
import com.entity.UserEntity;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class AnnotationTest {
public static void main(String[] args) throws IOException {
// 读取配置文件
InputStream stream = Resources.getResourceAsStream("com/SqlMapperConfig.xml");
// 使用SQLSession工厂构建器,创建SQLSession
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = factoryBuilder.build(stream);
// 获取SQLSession对象
SqlSession session = factory.openSession();
// 获取接口的代理对象(不用实现接口)
//region 获取用户数据
IUserDao userDao = session.getMapper(IUserDao.class);
List<UserEntity> userList = userDao.findAll();
for (UserEntity userEntity : userList) {
System.out.println("=====每个用户数据=====");
System.out.println(userEntity);
}
//endregion
//region 获取分数数据
// IScoreDao scoreDao = session.getMapper(IScoreDao.class);
// List<ScoreEntity> scoreList = scoreDao.findByUserId(123);
// for (ScoreEntity scoreEntity : scoreList) {
// System.out.println("===每类课程数据====");
// System.out.println(scoreEntity);
// }
//endregion
}
}
执行获取用户数据代码块结果如下:
执行获取分数数据代码块结果如下:
发现一个问题,图中红色部分的字段值要么为0,要么是null,通过上面的数据表可看出,数据库中是有数据的,那为什么没有获取到数据呢?
这是因为mybatis有个要求,实体中定义的字段名称必须和数据表中字段名称保持一致(大小写可忽略),名称一致的字段被读取到了,因此给赋值了,名称不一致的字段没有被读取到,就给了默认值,String类型默认null,Integer类型默认0.
那怎么将自己定义的字段与数据库字段一一对应拿起来,除了名称保持一致,还有什么办法呢?
使用@Results注解,在@select后,加上@Results,先来看一下@Results 和 @Result源码
@Results源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Results {
String id() default "";
Result[] value() default {};
}
有id 和 value属性,
id是一个唯一标识的名称
value 它的类型Result[]类型,用于存放映射字段关系
@Result源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Repeatable(Results.class)
public @interface Result {
boolean id() default false;
String column() default "";
String property() default "";
Class<?> javaType() default void.class;
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
One one() default @One;
Many many() default @Many;
}
通过查看@Result注解源码发现,它有id,column,property……属性
id表示主键,默认为false;
column表示列,即数据表的列;
property表示属性,即实体中定义的字段;
因此我们就可以用它来进行字段映射
@Select("select * from t_user")
@Results(id = "userMap",value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "role",property = "role"),
@Result(column = "name",property = "name"),
@Result(column = "email",property = "email"),
@Result(column = "name",property = "name"),
@Result(column = "password",property = "pwd"),
})
List<UserEntity> findAll();
@Select("select * from t_user where id=#{id}")
@ResultMap(value = "userMap")
UserEntity findById(Integer id);
这里又产生一个问题 @Results注解是写在findAll()方法上,那findById(Integer id)方法怎么办?又要重新写一遍映射?
有@ResultMap以供使用,在@Results中有id属性,它是唯一标识,给它取个名,其它需要用到这个映射的地方直接用id即可,例如:findById(Integer id)方法上的@ResultMap(value = “userMap”)
映射完成后再来执行测试程序,看一下结果:
可以看到,数据全部获取成功。
看到这里,又产生一个问题,这一顿操作都是针对单表啊,实际情况肯定最起码都是2个表以上联查,这怎么办?
往下看,使用mybatis进行一对一,一对多的联查
多表联合
- 一对一:一个学科的分数只能是属于一个用户的,分数和用户的关系就是一对一
- 一对多:一个用户可以有多个科目的分数,用户和分数的关系就是一对多
- 即时加载:即查询时候一次性将所有数据全部取出,例如
- 延时加载:需要多少数据就获取多少数据
例如当要查看用户信息,用户又关联的有分数数据,那么就可以先查询用户信息,需要查看分数时,再查相关的分数数据,这就是延时加载的体现,减少不必要的查询消耗,提升效率。如果采用即时加载,查看一个用户信息时候,会把这个用户相关联的分数也会查询出来,假如关联的分数数据有十万、百万级,而又只看了用户信息,没看关联的分数,但是后台却又把这百万级数据给加载出来,这会大大影响体验。
mybatis实现一对一查询
使用@one注解实现一对一,例如图中红色部分
@one源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface One {
String select() default "";
FetchType fetchType() default FetchType.DEFAULT;
}
select属性也是注解,它用来查询关联的表,这里可写sql语句,可调用方法,调用方法时,这里填写的一定是方法的全限定路径,由包名、类名、方法名组成。
fetchType这个属性指定了加载方式,即延时加载 和 即时加载
根据分数查询用户,在ScoreEntity中增加UserEntity类型的属性,每类课程数据只对应了一个用户,结果如下
mybatis实现一对多查询
使用@many注解,如图中红色部分
@many源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Many {
String select() default "";
FetchType fetchType() default FetchType.DEFAULT;
}
源码与@one相同,区别在于sql语句的返回值,因此当返回多条数据时,使用@many
根据用户查询分数,在UserEntity中增加List类型属性,由于用户和分数时一对多关系,查询结果如下:
可以看到用户123,124都关联了两条分数数据,用户125关联了1条,这就是一对多的体现。
注意事项
mybatis有两种开发方式,除了本文介绍的注解开发之外,另一种是对每个dao接口进行sql的配置,然而这两种方式不能并存,只能选择一种方式进行开发,否则在程序编译期间,会抛出找不到dao接口映射的异常
上一篇: 递归查询的几种方式
下一篇: ionic2中使用自动生成器的方法
推荐阅读
-
asp.net abp模块化开发之通用树2:设计思路及源码解析
-
SpringBoot 2.0 开发案例之百倍级减肥瘦身之旅
-
JSP开发之Struts2实现下载功能的实例
-
IOS开发(74)之把 Array 和 Dictionaries 序列化成 JSON 对象
-
IOS开发(63)之GCD执行延迟操作
-
IOS开发(66)之构建自己的分派队列
-
IOS开发(64)之GCD任务最多只执行一次
-
IOS开发(77)之iOS高级内存管理:比较__unsafe_unretain、__strong、__weak、__autoreleasing
-
IOS开发(80)之画线
-
IOS开发(76)之 NSNotification的使用