三.MyBatis
配置依赖
添加MyBatis的依赖
1
2
3
4
5<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>添加MyBatis与Spring整合依赖:
1
2
3
4
5<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>添加JDBC相关的Spring
版本必须与当前项目使用的
spring-webmvc
的版本完全相同。1
2
3
4
5<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>添加MySQL驱动和数据库连接池的依赖:
1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>Junit内注解测试
1
2
3
4
5
6
7
8
9
10
11
12<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.9.RELEASE</version>
<scope>test</scope>
</dependency>1
2
3(SpringRunner.class)
"classpath:spring.xml"}) (locations={
"classpath*:spring.xml","classpath*:spring_redis.xml"}) (locations= {
数据库流程
1. 配置并读取数据库文件[之前写过]
配置数据库文件
db.properties
1
2
3
4
5
6url=jdbc:mysql://localhost:3306/tedu_umsuseUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
driver=com.mysql.jdbc.Driver
username=root
password=root
initialSize=2
maxActive=10spring.xml
1
<util:properties id="dbConfig" location="classpath:db.properties" />
2.连接数据库
spring.xml
直接在BasicDataSource中查找属性;
建立BasicDataSource与数据库配置文件之间的联系;
name:BasicDataSource;value:数据库配置文件
1
2
3
4
5
6
7
8<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="#{dbConfig.url}" />
<property name="driverClassName" value="#{dbConfig.driver}"></property>
<property name="username" value="#{dbConfig.username}" />
<property name="password" value="#{dbConfig.password}" />
<property name="initialSize" value="#{dbConfig.initialSize}" />
<property name="maxActive" value="#{dbConfig.maxActive}" />
</bean>
*测试连接数据库
1 |
|
3. 持久层接口
创建实体类
用于被接口调用,并向其添加用于添加数据库的字段值
1
2
3
4public class User{
private 数据类型 属性名;
生成get/set/toString方法
}创建持久层接口
调用User类,用于向数据库中添加记录
增删改时返回值用 Insteger:用于返回修改的条目
1
2
3public interface UserMapper(){
Integer insert(User user);
}扫描持久层接口
1
2
3<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.tedu.mybatis" />
</bean>
4. 映射Sql语句
下载并使用mapper.xml文件
mapper.xml基础内容
1
2
3配置执行命令
mapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<mapper namespace="cn.tedu.mybatis.UserMapper">
<insert id="持久接口方法名">//节点错误时依然生效,但是返回值出错
INSERT INTO t_user (
<!--属性顺序可以与mysal字段循序不一致,必须与musal字段名一致,可以与user属性不相同但是必须对应-->
username, password,
age, phone,
email
) VALUES (
<!--user属性名 -->
#{username},#{password},
#{age},#{phone},
#{email}
)
</insert>
</mapper>调用并执行mapper.xml文件
spring.xml
直接在SqlSessionFactoryBean中配置
1
2
3
4
5
6<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源:连接与数据源的关系:BasicDataSource-->
<property name="dataSource" ref="dataSource" />
<!-- XML位置 -->
<property name="mapperLocations" value="classpath:mappers/*.xml" />
</bean>
*测试添加条目
注意事项:
- Spring能创建接口对象(通过代理对象的方法)
- 在Spring中可以通过set方法/自己书写/接收表单数据接收数据
- 默认spring只创建对象,但是不执行里面的方法
java
1
2
3
4
5
6
7
8
9
10
11
12
13
UserMapper userMapper = ac.getBean("userMapper", UserMapper.class);
//也可以在Spring.xml文件中配置
//User user = ac.getBean("user",User.class);
User user = new User();
user.setUsername("admin");
user.setPassword("1234");
user.setAge(18);
user.setPhone("12345678901");
user.setEmail("admin@tedu.com");
//方法默认不执行,因此需要手动调用方法
Integer rows = userMapper.insert(user);
System.out.println("rows="+rows);
基础操作
1. 查询
@insert(sql语句)
思路
- 思路1:直接在xml上配置值;[不建议使用,不能复用]
- 思路2:在街口抽象方法上加入参数:
- 思路2.1:参数类型是String
- 思路2.2:参数类型User[不生效]
- 思路2:在接口抽象方法上加入参数(可以复用);
- 思路2.1:返回值类型是String[推荐]
- 思路2.2:返回值类型是User[User内的参数要包含查询语句参数]
注意事项
- resultType是接口类返回值类型的类地址(数组类型是单个类型的地址)
- 返回值类型可以是任意的,resultType在写lang类型时可以省略
- 除了添加数据以外其余的不能使用java实体类传入数据
查询整个列表
接口类
1
List<User> findAll();//配置一个抽象方法,返回值为列表
mapper.xml
1
2
3<select id="对应抽象方法" resultType="返回实体类地址">
SELECT * FROM t_user
</select>test
1
2
3for(User user : list){
system.out.println(user.toString());
}
查询一条数据
接口类
1
User findOne(String username);//
mapper.xml
1
2
3
4<select id="findOne">
SELECT username FROM t_user WHERE username=#{username};
<!--#{}内代表传入的值,接口值默认传入-->
</select>test
1
2User user = userMapper.findByUsername("root");
system.out.println(user);
查询单个字段值
2. 接口名称和字段名称不相同
通常情况下不会有增加字段操作,因此个人不推荐这种做法
执行mysql命令增加新字段[只能在mysql中执行命令添加]
1
alter table t_user add column is_delete int;
实体类[额外操作]
1
2private Integer isDelese;
增加get set方法,重新修改toString方法接口类[额外操作]
1
Integer addAlter();//返回这类型Integer:是否成功;参数列表:不添加不用写
mapper.xml[额外操作]
修改之前用到字段名的标签,增加没有写的新字段
1
2
3
4
5
6
7
8
9
10
11<insert id="持久接口方法名">//节点错误时依然生效,但是返回值出错
INSERT INTO t_user (
username, password,
age, phone,
email,is_delete<!--字段名称-->
) VALUES (
#{username},#{password},
#{age},#{phone},
#{email},#{isDelete}<!--属性名称-->
)
</insert>
注意事项:
出现问题
当出现了字段名与实体类中的属性名不一致时,查询时的字段值显示为null
解决方法
不能使用select * 了,因为不能添加别名,生产情况中*不常用
1
2
3<select id="findAll" resultAll="cn.tedu.mybatis.User">
select username, password, age, phone, email,is_delete as isDelete from t_user;
</select>
3. 更新字段值
接口类
1
Integer updateIsDelete(Integer isDelete);
mapper.xml
1
2
3<update id="updateIsDelete">
UPDATE t_user SET is_delete = #{isDelete}
</update>Test
1
2
3
4
public void a(){
Integer rows = 接口类对象.updateIsDelete(0);
}
4.传入多个参数
常规思路[错误的演示]
接口类
1
Integer updateUandP(String username,String password);
mapper.xml
1
2
3<update id="updateUandP">
UPDATE t_user SET password = #{password} WHERE USERNAME = #{username}
</update>test
1
2
3
4
public void updateUandP(){
Integer rows = 接口对象.updateUandP("root","123456");
}错误提示
1
2Caused by: org.apache.ibatis.binding.BindingException: Parameter 'password' not found.
Available parameters are [arg1, arg0, param1, param2]原因
MyBatis框架只识别一个参数:默认传入四个参数,三个是框架用的不可见,一个是用户使用的,
使用Map传入两个参数[不完善,不推荐]
- 必须知道map传入了几个数值,传入的类型,局限性太大,太麻烦因此不推荐
接口类
1
Integer updateUandP(SMap map);
mapper.xml
参数名必须与map中键名相同
1
2
3<update id="updateUandP">
UPDATE t_user SET password = #{password1} WHERE USERNAME = #{username1}
</update>test
1
2
3
4
5
6
7
public updateUandp2(){
Map map = new HashMap<String,Object>();
map.put("password1","longda101");
map.put("username1","root103");
Integer rows = 接口对象.updateUandP2(map);
}
注解
接口类
1
Integer updateUandP3(@param("username3") String password3,@Param("password3") String password3);
mapper.xml
参数名必须与map中键名相同
1
2
3<update id="updateUadnp3">
UPDATE t_user SET password = #{password3} WHERE USERNAME = #{username3}
</update>test
1
2
3
4
public updateUandp3(){
Integer rows = 接口对象.updateUandP3("root103","longdan103");
}
5. 获取新增记录的自增长ID
接口类
1
Integer insertAutoId();
mapper.xml
useGeneratedKeys=”true”:启动获取新增记录自增长ID
keyProperty=”参数名”:返回到实体类对象的一个参数值中,替换原来的值
1
2
3<insert useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user(username,password) VALUES(#{username},#{password})
</insert>test
两次ID的值不一样,第二次为返回的值
1
2
3
4
5
6
7
8
9
public void insertAutoId(){
User user = new User();
user.setUsername("rootx");
user.setPassword("rootx");
syso(user.getID());//此时user中的ID为X
Insert rows = 接口对象.insertAutoID();
syso(user.getID());//此时user中的ID为Y
}
6.统计当前数据库中数据的数量
类似于思路2.1的方法
接口
1
Integer countID();
mapper.xml
1
2
3<select id="countID" resultType="Integer">
SELECT COUNT(id) FROM t_user;
</select>test
1
2
3
4
5
public void countID(){
Integer countID = userMapper.countID();
System.out.println(countID);
}
7. 多表关联查询
因为没有能与关联查询相匹配的实体类,因此我们在这里使用VO类
新建VO类
1
2
3
4
5public void UserVO(){
user类属性
department类属性
生成get/set/toString方法
}接口
1
List<UserVO> findAll2();
mapper.xml
1
2
3<select id="findAll2" resultType="cn.tedu.mybatis.UserVO">
SELECT t_user.id, username,password, age,is_delete AS isDelete,department_id,name FROM t_user LEFT JOIN t_department ON t_user.department_id=t_department.id
</select>test
1
2
3
4List<UserVO> list = userMapper.findAll2();
for(UserVO userVO : list){
System.out.println(userVO);
}
动态SQL
目前理解为参数为多个相同类型,不能直接传或者map或者@param
1. <foreach>标签
属性 | 作用 | 选项 | 状态 |
---|---|---|---|
collection | 传入类型 | list/array/map/set | 必选 |
item | 别名,相当于id键 | 必选 | |
index | 迭代次数,值 | 可选 | |
separator | 为传入的数据之间添加分隔符 | “,” | 可选 |
open | foreach开启之前的sql语句 | “(“ | 可选/[不推荐] |
close | foreach开始之后的sql语句 | “)” | 可选/[不推荐] |
sql语句
1
DELETE FROM t_user WHERE id IN (2,4,5);
接口
因为是多个相同类型,这里建议用数组或者集合
1
Integer deleteByIDs(Integer[] ids);//数组
mapper.xml
内容
1
2
3
4
5
6<delete id="deleteByIDs">
DELETE FROM t_user WHERE id IN
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>详解
1
2
3for(Integer item : (collection)ids){
System.out.print(item);
}
test
1
2
3
4
5
6
public void DeleteByID(){
Integer[] ids = {3,5,7};
Integer rows = userMapper.deleteByIDs(ids);
System.out.println(rows;
}
2. <if>标签
抽象类
1
List<User> findIf(@pParam("where") String whereX);
mapper.xml
1
2
3
4
5
6
7
8<select id="findIf">
SELECT * FROM t_user
<if test="where != null"//if
WHERE ${where}
</if>
<if test="where == null">//else,在本题目中可以不写
</if>
</select>test
在传入值是一个参数时用#{},当传入值是一段话时用${}
JDBC中的?处理一个参数时不用加引号,因为是预编译,预编译不支持引号,即便是or语句也会识别成一个参数,因此在这里不能用#{},只能使用${}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void findIf(){
syso("where != null");
String whereX = "username = 'root'";
List<User> list1 = 接口类对象.findIf(whereX);
for(User user : list1){
syso(user);
}
syso("where == null");
List<User> list2 = 接口类对象.findIf(whereX);
for(User user: list2){
syso(user);
}
}
resultMap
将查询到的结果封装到对象中
错误的演示[错误]
新建VO类
1
2
3private Integer id;
private String name;
private List<User> user;//此处引用了一个对象,而不是对象内的属性接口
1
DepartmentVO findById(Integer id);
mapper.xml
1
2
3<select id="findById" resultType="cn.tedu.myBatis.DepartmentVO">
SELECT t_department.id AS did,t_department.name,t_user.id AS uid,username, password,age, phone,email, is_delete,department_id FROM t_department LEFT JOIN t_user ON t_department.id=t_user.department_id WHERE t_department.id=#{id};
</select>错误提示
因为在VO类中引用了User对象,而在返回时查询到的数据不能封装到User对象中,因而报错
1
Caused by: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
resultMap节点
需要封装到对象中的数据写在collection子节点中,不需要的直接写在resultMap节点中
节点 作用 注意 id id名 resultMap属性 type 查询返回值类型地址 resultMap属性 collection 用于配置1:N关系的数据(需要封装到对象中的数据) resultMap子节点 ofType 集合中的元素的类型地址 collection属性 id 用于配置主键字段的值的封装 collection子节点/resultMap子节点 result 用于配置非主键字段的封装 collection子节点/resultMap子节点 column mysql查询的字段名(有别名时为别名) id/result属性 property 要封装的属性名 id/result属性 注意事项
- 此处属性值和字段名不相同时不需要写别名,只有在单纯查询时需要写,这里的resultMap已经写了关系,所以不用写
- select的resultType节点改为resultMap节点
- 可以只使用result,而不使用id,但是推荐使用,因为mybatis会基于id的配置查询结果的缓存
- 当有两个相同的属性值时(正常属性值和导入属性值相同时)需要修改别名
- 添加<catch/>可以进行缓存,增删改删除缓存,缓存方便查找,因此经常增删改不应该加缓存
正确的方法
节点
1
DepartmentVO findById(Integer id);
mapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<select id="findById" resultMap="DepartmentVO_Map">//使用新节点
SELECT t_department.id AS did,t_department.name,t_user.id AS uid,username, password,age, phone,email, is_delete,department_id FROM t_department LEFT JOIN t_user ON t_department.id=t_user.department_id WHERE t_department.id=#{id};
</select>
<resultMap id="DepartmentVO_Map" type="cn.tedu.mybatis.DepartmentVO" >
<id column="did" property="id" />
<result column="name" property="name" />
<collection property="需要封装到的对象名" ofType="cn.tedu.mybatis.User">
<id column="uid" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="age" property="age" />
<result column="phone" property="phone" />
<result column="email" property="email" />
<result column="is_delete" property="isDelete" />
<result column="department_id" property="departmentId" />
</collection>
</resultMap>test
1
2DepartmentVO departmentVO = departmentMapper.findById(3);
System.out.println(departmentVO);
最后更新: 2019年11月06日 11:36