1.使用动态代理来实现mybatis。
动态代理的实现规范:
- UsersMapper.xml文件与UsersMapper.java的接口必须同一个目录下.
- UsersMapper.xml文件与UsersMapper.java的接口的文件名必须一致,后缀不管.
- UserMapper.xml文件中标签的id值与与UserMapper.java的接口中方法的名称完全一致.
- UserMapper.xml文件中标签的parameterType属性值与与UserMapper.java的接口中方法的参数类型完全一致.
- UserMapper.xml文件中标签的resultType值与与UserMapper.java的接口中方法的返回值类型完全一致.
- UserMapper.xml文件中namespace属性必须是接口的完全限定名称com.bjpowernode.mapper.UsersMapper
- 在SqlMapConfig.xml文件中注册mapper文件时,使用class=接口的完全限定名称com.bjpowernode.mapper.UsersMapper.
接口和xml文件在同一目录且名字相同,xml文件就是UserMapper接口的sql语句实现文件,所以
接口中的所有方法名称与id对应,然后namespace命名空间对应接口的位置,最后到mybatis总的配置文件注册一下就行了
<mapper namespace="com.mapper.UserMapper">
<select id="getAll" resultType="student">
select id,name,email,age
from student
</select>
<select id="getLickByName" resultType="student" parameterType="string">
select id,name,email,age
from student
where name like '%${name}%'
</select>
<insert id="addStudent" parameterType="student">
insert into student(name, email, age)
value(#{name},#{email},${age})
</insert>
<update id="updateByName" parameterType="student">
update student set email=#{email},age=#{age}
where name=#{name}
</update>
</mapper>
public interface UserMapper {
List<Student> getAll();
List<Student> getLickByName(String name);
int addStudent(Student student);
int updateByName(Student student);
}
注册配置文件:分为单个文件注册和批量注册
<mappers>
<!-- <mapper class="com.mapper.UserMapper"></mapper>-->
<package name="com.mapper"/>
<!-- 包名一次性注册-->
</mappers>
测试的时候任然需要将sqlsession对象取出来,但是可以通过这个获取代理对象usermapper
SqlSession sqlSession;
UserMapper userMapper;
@Before
public void Before() throws IOException {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
userMapper=sqlSession.getMapper(UserMapper.class);
}
@After
public void After(){
sqlSession.close();
}
@Test
public void getAll(){
// userMapper.getAll();
//
// List<Student> list = userMapper.getLickByName("李");
// list.forEach(student -> System.out.println(student));
Student student=new Student("李四","shagou@qq.com",54);
// userMapper.addStudent(student);
userMapper.updateByName(student);
sqlSession.commit();
}
}
1.2.#{}占位符
传参大部分使用#{}传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问 ,防止sql注入.
#{}里如何写,看parameterType参数的类型
1)如果parameterType的类型是简单类型(8种基本(封装)+String),则#{}里随便写.
<select id="getById" parameterType="int" resultType="users"> ===>入参类型是简单类型
select id,username,birthday,sex,address
from users
where id=#{zar} ===>随便写
</select>
2)parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写.
<insert id="insert" parameterType="users" > ===>入参是实体类
insert into users (username, birthday, sex, address) values(#{userName},#{birthday},#{sex},#{address}) ==>成员变量名称
</insert>
1.3${}字符串拼接或字符串替换
1)字符串拼接,一般用于模糊查询中.建议少用,因为有sql注入的风险.
也分两种情况,同样的看parameterType的类型
A. 如果parameterType的类型是简单类型,则${}里随便写,但是分版本,如果是3.5.1及以下的版本,只以写value.
<select id="getByName" parameterType="string" resultType="users"> ===>入参是简单类型
select id,username,birthday,sex,address
from users
where username like '%${zar}%' ===>随便写
</select>
B. 如果parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称.(现在已经少用)
C. 优化后的模糊查询(以后都要使用这种方式)
<select id="getByNameGood" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like concat('%',#{name},'%')
</select>
多个参数传递注册的情况
多个参数不用输入注册,但是需要再接口的地方使用注解来定义一下,不然无法接收数据。
如果不使用xml,去使用注解,就可以不需要这个注解,参数直接再最底层的接口实现
//接口
List<Student> getLick(@Param("key")String key,@Param ("value") String value);
//xml,不用注册输入的类型
<select id="getLick" resultType="student">
select name,email,age
from student
where ${key} like concat('%',#{value},'%')
</select>
//直接使用
System.out.println(userMapper.getLick("name", "李"));
返回主键值
在插入语句结束后, 返回自增的主键值到入参的users对象的id属性中.
<insert id="insert" parameterType="users" >
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into users (username, birthday, sex, address) values(#{userName},#{birthday},#{sex},#{address})
</insert>
<selectKey>标签的参数详解:
keyProperty: users对象的哪个属性来接返回的主键值
resultType:返回的主键的类型
order:在插入语句执行前,还是执行后返回主键的值
UUID
这是一个全球唯一随机字符串,由36个字母数字中划线组.
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString().replace("-","").substring(20));
获取一个12位的随机数字字母组合
什么是动态sql
可以定义代码片断,可以进行逻辑判断,可以进行循环处理(批量处理),使条件判断更为简单.
1)<sql>:用来定义代码片断,可以将所有的列名,或复杂的条件定义为代码片断,供使用时调用.
2)<include>:用来引用<sql>定义的代码片断.
<!--定义代码片断-->
<sql id="allColumns">
id,username,birthday,sex,address
</sql>
//引用定义好的代码片断
<select id="getAll" resultType="users" >
select <include refid="allColumns"></include>
from users
</select>
3)<if>:进行条件判断
test条件判断的取值可以是实体类的成员变量,可以是map的key,可以是@Param注解的名称.
4)<where>:进行多条件拼接,在查询,删除,更新中使用.
<select id="getCondition" resultType="student" parameterType="student">
select <include refid="column"></include>
from student
<where>
<if test="name!=null and name!=''">
and name like concat('%',#{name},'%')
</if>
<if test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</if>
<if test="age!=null and age!=''">
and age like concat('%',#{age},'%')
</if>
</where>
</select>
动态查询,有这些条件就使用,没有就全部查询
这个查询的就是年龄带上1的数据,一般用于多个字段的搜索
Student student=new Student("","",1);
List<Student> li = userMapper.getCondition(student);
li.forEach(student1 -> System.out.println(student1));
动态修改
<update id="updateCondition" parameterType="student" >
update student
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="email!=null and email!=''">
email=#{email},
</if>
<if test="age!=null and age!=''">
age=#{age},
</if>
</set>
where id=#{id}
</update>
<foreach>:用来进行循环遍历,完成循环条件查询,批量删除,批量增加,批量更新.
查询实现
<select id="getByIdS" resultType="student">
select <include refid="column"></include>
from student
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<foreach>参数详解:
collection:用来指定入参的类型,如果是List集合,则为list,如果是Map集合,则为map,如果是数组,则为array.
item:每次循环遍历出来的值或对象
separator:多个值或对象或语句之间的分隔符
open:整个循环外面的前括号
close:整个循环外面的后括号
foreach用来批量增加
<insert id="addStudents" parameterType="list">
insert into student(name, email, age)
values
<foreach collection="list" item="s" separator=",">
(#{s.name},#{s.email},#{s.age})
</foreach>
</insert>
foreach用来更改:本质上是执行多条语句,所以需要开启权限
再jdbc文件中修改:最后的一个就是开启多条语句一起执行
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
<update id="updateStdents" parameterType="list">
<foreach collection="list" item="u" separator=";">
update student
<set>
<if test="u.name!=null">
name=#{u.name},
</if>
<if test="u.email!=null">
email=#{u.email},
</if>
<if test="u.age!=null">
age=#{u.age}
</if>
</set>
where id=#{u.id}
</foreach>
</update>
指定参数位置
如果入参是多个,可以通过指定参数位置进行传参. 是实体包含不住的条件.实体类只能封装住成员变量的条件.如果某个成员变量要有区间范围内的判断,或者有两个值进行处理,则实体类包不住.
例如:查询指定年龄范围内的用户信息.直接用这种占位符就行
<select id="getByAge" resultType="student">
select <include refid="column"></include>
from student
where age between #{arg0} and #{arg1}
</select>
入参是map(重点掌握)
如果入参超过一个以上,使用map封装查询条件,更有语义,查询条件更明确.直接使用map的key,mybatis可以直接拿到value值。
<select id="getByAgeMap" resultType="student">
select <include refid="column"></include>
from student
where age between #{begin} and #{end}
</select>
@Test
public void getagemap(){
Map<String,Integer> map=new HashMap<>();
map.put("begin",1);
map.put("end",15);
List<Student> i = userMapper.getByAgeMap(map);
i.forEach(student -> System.out.println(student));
sqlSession.commit();
}
.返回值是map
如果返回的数据实体类无法包含,可以使用map返回多张表中的若干数据.返回后这些数据之间没有任何关系.就是Object类型.返回的map的key就是列名或别名.
<!--
//返回值是map(一行)
Map getReturnMap(Integer id);
-->
<select id="getReturnMap" parameterType="int" resultType="map">
select username name,address a
from users
where id=#{id}
</select>
会返回{name=张三,a=213}
<!--
//返回多行的map
List<Map> getMulMap();
-->
<select id="getMulMap" resultType="map">
select username,address
from users
</select>
表之间的关联关系
关联关系是有方向的.
1)一对多关联:一个老师可以教多个学生,多个学生只有一个老师来教,站在老师方,就是一对多关联.
2)多对一关联:一个老师可以教多个学生,多个学生只有一个老师来教,站在学生方,就是多对一关联.
3)一对一关联:一个老师辅导一个学生,一个学生只请教一个老师.学生和老师是一对一.
4)多对多关联:园区划线的车位和园区的每一辆车.任意一个车位可以停任意一辆车.任意一车辆车可以停在任意一个车位上.
一对多实例
首先需要两张表
然后直接查询的效果
再实体类中定义一个包含另一个类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class students {
private Integer id;
private String name;
private String email;
private Integer age;
List<studentCourse> studentCourses;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class studentCourse {
private String name;
private Integer sno;
private String sex;
private String course;
}
在sql中使用的时候没办法直接使用students来承载,必须定义一个resultmap来存放
<resultMap id="students" type="students">
<id property="id" column="sid"></id>
<result property="name" column="sname"></result>
<result property="email" column="email"></result>
<result property="age" column="age"></result>
<collection property="studentCourses" ofType="studentCourse">
<result property="name" column="oname"></result>
<result property="sno" column="sno"></result>
<result property="sex" column="sex"></result>
<result property="course" column="course"></result>
</collection>
</resultMap>
<select id="getByName" parameterType="string" resultMap="students">
select s.id sid,s.name sname,email,age,o.name oname,sno,sex,course
from student_one o join student s on o.name=s.name
where o.name=#{name}
</select>
这个标签只是为了定义一个承载类型,collection集合用于存放循环的相同数据
最后直接执行就行
.多对一关联关系.
订单和客户就是多对一关联.
站在订单的方向查询订单的同时将客户信息查出.
订单是多方,会持有一方的对象.客户是一方.
<mapper namespace="com.bjpowernode.mapper.OrdersMapper">
<!--
//根据主键查询订单,并同时查询下此订单的客户信息
Orders getById(Integer id);
-->
<!--
手工绑定数据
实体类
private Integer id;
private String orderNumber;
private Double orderPrice;
//关联下此订单的客户信息,多方持有一方的对象
private Customer customer;
-->
<resultMap id="ordersmap" type="orders">
<!--主键绑定-->
<id property="id" column="oid"></id>
<!--非主键绑定-->
<result property="orderNumber" column="orderNumber"></result>
<result property="orderPrice" column="orderPrice"></result>
<!--多出来的一咕噜绑定
private Integer id;
private String name;
private Integer age;
//该客户名下的所有订单的集合,一方持有多方的集合
private List<Orders> ordersList; //不用管
-->
<association property="customer" javaType="customer">
<id property="id" column="cid"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
</association>
</resultMap>
<select id="getById" parameterType="int" resultMap="ordersmap">
select o.id oid,orderNumber,orderPrice,customer_id,c.id cid,name,age
from orders o inner join customer c on o.customer_id = c.id
where o.id=#{id}
</select>
</mapper>
值得注意的是,实体类中包含其他实体类使用association标签来包含
事务
多个操作同时完成,或同时失败称为事务处理.
事务有四个特性:一致性,持久性,原子性,隔离性.
下订单的业务:
1)订单表中完成增加一条记录的操作
2)订单明细表中完成N条记录的增加
3)商品数据更新(减少)
4)购物车中已支付商品删除
5)用户积分更新(增加)
在MyBatis框架中设置事务
<transactionManager type="JDBC"></transactionManager> ===>程序员自己控制处理的提交和回滚
可设置为自动提交
sqlSession = factory.openSession(); ===>默认是手工提交事务,设置为false也是手工提交事务,如果设置为true,则为自动提交.
sqlSession = factory.openSession(true); ===>设置为自动提交,在增删改后不需要commit();
.缓存
MyBatis框架提供两级缓存,一级缓存和二级缓存.默认开启一级缓存.
缓存就是为了提交查询效率.
使用缓存后,查询的流程:
查询时先到缓存里查,如果没有则查询数据库,放缓存一份,再返回客户端.下次再查询的时候直接从缓存返回,不再访问数据库.如果数据库中发生commit()操作,则清空缓存.
一级缓存使用的是SqlSession的作用域,同一个sqlSession共享一级缓存的数据.
二级缓存使用的是mapper的作用域,不同的sqlSession只要访问的同一个mapper.xml文件,则共享二级缓存作用域.
什么是ORM
ORM(Object Relational Mapping):对象关系映射
MyBatis框架是ORM非常优秀的框架.
java语言中以对象的方式操作数据,存到数据库中是以表的方式进行存储,对象中的成员变量与表中的列之间的数据互换称为映射.整个这套操作就是ORM.
持久化的操作:将对象保存到关系型数据库中 ,将关系型数据库中的数据读取出来以对象的形式封装
MyBatis是持久化层优秀的框架.