<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>mybatis_annotaionartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>jarpackaging>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.4version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.16version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
dependencies>
project>
<configuration>
<properties resource="com/jdbcConfig.properties">properties>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc">transactionManager>
<dataSource type="POOLED">
<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
用户信息实体
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 +
'}';
}
}
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 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进行一对一,一对多的联查
例如当要查看用户信息,用户又关联的有分数数据,那么就可以先查询用户信息,需要查看分数时,再查相关的分数数据,这就是延时加载的体现,减少不必要的查询消耗,提升效率。如果采用即时加载,查看一个用户信息时候,会把这个用户相关联的分数也会查询出来,假如关联的分数数据有十万、百万级,而又只看了用户信息,没看关联的分数,但是后台却又把这百万级数据给加载出来,这会大大影响体验。
@one源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface One {
String select() default "";
FetchType fetchType() default FetchType.DEFAULT;
}
select属性也是注解,它用来查询关联的表,这里可写sql语句,可调用方法,调用方法时,这里填写的一定是方法的全限定路径,由包名、类名、方法名组成。
fetchType这个属性指定了加载方式,即延时加载 和 即时加载
根据分数查询用户,在ScoreEntity中增加UserEntity类型的属性,每类课程数据只对应了一个用户,结果如下
@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接口映射的异常