0、原始JDBC API操作
0.1、简单操作步骤
(1)导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
(2)编写代码
@Test
public void selectTest() throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/jdbc_template_test", "root", "553057712");
//获取statement
PreparedStatement statement = connection.prepareStatement("select * from user");
//执行查询
ResultSet resultSet = statement.executeQuery();
//查询的结果集
List<User> users = new ArrayList<>();
//遍历结果
while(resultSet.next()){
User user = new User();
String name = resultSet.getString("name");
user.setName(name);
int age = resultSet.getInt("age");
user.setAge(age);
users.add(user);
}
System.out.println(users);
//释放资源
resultSet.close();
statement.close();
connection.close();
}
@Test
public void insertTest() throws ClassNotFoundException, SQLException {
//插入对象
User user = new User("朱元璋",49);
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/jdbc_template_test", "root", "553057712");
//获取statement
PreparedStatement statement = connection.prepareStatement("insert into user(name,age) values(?,?)");
statement.setString(1,user.getName());
statement.setInt(2,user.getAge());
//执行更新操作
int resultCount = statement.executeUpdate();
System.out.println(resultCount);
//释放资源
statement.close();
connection.close();
}
@Test
public void updateTest() throws ClassNotFoundException, SQLException {
//更新对象
User user = new User("朱元璋",60);
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/jdbc_template_test", "root", "553057712");
//获取statement
PreparedStatement statement = connection.prepareStatement("update user set age=? where name=?");
statement.setInt(1,user.getAge());
statement.setString(2,user.getName());
//执行更新操作
int resultCount = statement.executeUpdate();
System.out.println(resultCount);
//释放资源
statement.close();
connection.close();
}
@Test
public void deleteTest() throws ClassNotFoundException, SQLException {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/jdbc_template_test", "root", "553057712");
//获取statement
PreparedStatement statement = connection.prepareStatement("delete from user where name=?");
statement.setString(1,"李白");
//执行更新操作
int resultCount = statement.executeUpdate();
System.out.println(resultCount);
//释放资源
statement.close();
connection.close();
}
0.2、原始JDBC存在的问题与解决方案
问题:
- 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
- sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
- 查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位符位置
解决方案:
- 使用数据库连接池初始化连接资源
- 将sql语句抽取到xml配置文件中
- 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
1、Spring JDBC Template
1.0、概述
它是spring框架中提供的一个对象,是对原始繁琐的JdbcAPI对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。
1.2、JdbcTemplate开发步骤
- 导入spring-jdbc和spring-tx坐标
- 创建数据库表和实体
- 创建JdbcTemplate对象
- 执行数据库操作
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
public void test1(){
//数据源
MysqlDataSource mysqlDataSource = new MysqlConnectionPoolDataSource();
mysqlDataSource.setServerName("localhost");
mysqlDataSource.setDatabaseName("jdbc_template_test");
mysqlDataSource.setUser("root");
mysqlDataSource.setPassword("553057712");
//创建模板对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(mysqlDataSource);
//执行操作
int row = jdbcTemplate.update("insert into user values(?,?)", "hwz", 33);
System.out.println(row);
}
1.3、JdbcTemplate整合spring
编写jdbc.properties文件
serverName=localhost
databaseName=jdbc_template_test
user=root
password=553057712
在applicationContext.xml中注册 JdbcTemplate到IOC容器中
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 数据源 -->
<bean id="dataSource" class="com.mysql.cj.jdbc.MysqlConnectionPoolDataSource">
<property name="serverName" value="${serverName}"/>
<property name="databaseName" value="${databaseName}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
测试
@Resource(description = "jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
public void test2(){
int row = jdbcTemplate.update("insert into user values(?,?)", "hwz", 33);
System.out.println(row);
}
1.4、JdbcTemplate的CRUD
@Test
public void testInsert(){
int row = jdbcTemplate.update("insert into user values(?,?)", "朱自清", 33);
System.out.println(row);
}
@Test
public void testUpdate(){
int row = jdbcTemplate.update("update user set age=? where name=?",18,"hwz");
System.out.println(row);
}
@Test
public void testDelete(){
int row = jdbcTemplate.update("delete from user where name=?","hwz");
System.out.println(row);
}
@Test
public void testQueryAll(){
List<User> users = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<User>(User.class));
System.out.println(users);
}
@Test
public void testQueryOne(){
User user = jdbcTemplate.queryForObject("select * from user where name=?", new BeanPropertyRowMapper<User>(User.class), "李白");
System.out.println(user);
}
@Test
public void testQueryCount(){
Integer count = jdbcTemplate.queryForObject("select count(*) from user", Integer.class);
System.out.println(count);
}
2、Mybatis
MyBatis中文网 中文网
2.0、什么是Mybatis
- mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
- mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
- 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM(对象关系映射)思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
2.1、Mybatis 快速入门
开发步骤:
- 添加MyBatis的坐标
- 创建user数据表
- 编写User实体类
- 编写映射文件UserMapper.xml
- 编写核心文件SqIMapConfig.xml
- 编写测试类
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
<select id="findAll" resultType="org.example.entity.User">
select * from user
</select>
</mapper>
注意:UserMapper.xml文件位置规范,最好是放在主包路径下(一般配置文件才放在类路径下的,这样是为了更好的区分配置文件和映射文件,更好管理和维护)
这样他的打包路径是这样的:
SqIMapConfig.xml配置文件
<?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>
<!-- 数据源环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/jdbc_template_test"/>
<property name="username" value="root"/>
<property name="password" value="553057712"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="org/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
测试:
@Test
public void test() throws IOException {
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace+id
List<User> userList = sqlSession.selectList("userMapper.findAll");
System.out.println(userList);
//释放资源
sqlSession.close();
resourceAsStream.close();
}
Mybatis 映射文件概述
2.2、Mybatis 增删改查
快速开发已经做过查询了
增加(插入):
<!-- 插入操作 -->
<insert id="save" parameterType="org.example.entity.User">
insert into user values(#{name},#{age})
</insert>
@Test
public void test2() throws IOException {
//插入对象
User user = new User("司马懿", 57);
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace+id
int count = sqlSession.insert("userMapper.save", user);
System.out.println(count);
//注意:mybatis默认更新操作不提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
resourceAsStream.close();
}
修改:
<!-- 修改操作 -->
<update id="update" parameterType="org.example.entity.User">
update user set age=#{age} where name=#{name}
</update>
@Test
public void test3() throws IOException {
//插入对象
User user = new User("司马懿", 80);
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace+id
int count = sqlSession.update("userMapper.update", user);
System.out.println(count);
//注意:mybatis默认更新操作不提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
resourceAsStream.close();
}
删除:
<!-- 删除操作 单个参数时,#{随便一个值}一般写id -->
<delete id="delete" parameterType="string">
delete from user where name=#{1}
</delete>
@Test
public void test4() throws IOException {
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace+id
int count = sqlSession.delete("userMapper.delete", "司马懿");
System.out.println(count);
//注意:mybatis默认更新操作不提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
resourceAsStream.close();
}
sqlSessionFactory.openSession(true);事务自动提交
2.3、Mybatis 核心配置文件概述
2.3.1、environments标签
数据库环境的配置,支持多环境配置
其中,事务管理器(transactionManager)类型有两种:
- JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
- MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为 false来阻止它默认的关闭行为。
其中,数据源(dataSource)类型有三种:
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。
- JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
2.3.2、mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
- 使用相对于类路径的资源引用,例如: <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
- 使用完全限定资源位符(URL),例如: <mapper url="file:///var/mappers/AuthorMapper.xml" >
- 使用映射器接口实现类的完全限定类名,例如: <mapper class="org.mybatis.builder.AuthorMapper'/>
- 将包内的映射器接口实现全部注册为映射器,例如: <package name="org.mybatis.builder"/>
2.3.3、properties标签
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
<!-- 通过properties标签加载外部properties文件 -->
<properties resource="jdbc.properties"/>
<!-- 数据源环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
2.3.4、properties标签
<!-- 定义别名 -->
<typeAliases>
<typeAlias type="org.example.entity.User" alias="user"/>
</typeAliases>
使用可以直接用别名
<update id="update" parameterType="user">
update user set age=#{age} where name=#{name}
</update>
注意配置标签是有循序的:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
2.4、mybatis代理开发方式
2.4.1、简介
采用Mybatis的代理开发方式实现DAO层的开发,这种方式是我们后面进入企业的主流。Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接
口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的namespace与mapper接口的全限定名相同
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
在dao层编写接口UserMapper
public interface UserMapper {
List<User> findByName(String name);
}
映射文件UserMapper.xml
<mapper namespace="org.example.dao.UserMapper">
<select id="findByName" parameterType="string" resultType="user">
select * from user where name=#{name}
</select>
</mapper>
测试(模拟service层)
@Test
public void test6() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper代理对象(相当于dao的实现类)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findByName("朱元璋");
System.out.println(users);
sqlSession.close();
resourceAsStream.close();
}
2.5、mybatis映射文件的深入
2.5.1、动态sql
- if
- choose (when, otherwise)
- trirn (where, set)
- foreach
if(1+1老方法)
List<User> findByCondition(User user);
<select id="findByCondition" parameterType="user" resultType="user">
select * from user where 1=1
<if test="name!=null">
and name=#{name}
</if>
<if test="age>0">
and age=#{age}
</if>
</select>
if(where标签方法)
<select id="findByCondition" parameterType="user" resultType="user">
select * from user
<where>
<if test="name!=null">
and name=#{name}
</if>
<if test="age>0">
and age=#{age}
</if>
</where>
</select>
foreach
List<User> findByNames(List<String> names);
select * from user where name in(?,?)
<select id="findByNames" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="list" open="name in(" close=")" separator="," item="name">
#{name}
</foreach>
</where>
</select>
2.5.2、sql 语句抽取
<!-- sql语句抽取 -->
<sql id="selectUser">select * from user</sql>
<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"></include>
<where>
<if test="name!=null">
and name=#{name}
</if>
<if test="age>0">
and age=#{age}
</if>
</where>
</select>
2.6、mybatis核心配置文件的深入
2.6.1、typeHandlers标签
无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。下表描述了一些默认的类型处理器(截取部分)。 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现
org.apache.ibatis.type.TypeHandler 按口,或继承一个子实现类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型。
例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
开发步骤:
- 定义转换类继承类BaseTypeHandler<T>
- 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成java的Type类型的方法
- 在MyBatis核心配置文件中进行注册
- 测试转换是否正确
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String dateStr) {
//将日期字符串转换成日期对象,返回
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
<!-- 自定义类型处理器 -->
<typeHandlers>
<typeHandler handler="org.example.handler.DateTypeHandler"/>
</typeHandlers>
2.6.2、plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装。使甲简单的方式即可获得分页的相关数据
开发步骤:
- 导入通用PageHelper的坐标
- 在mybatis核心配置文件中配置PageHelper插件
- 测试分页数据获取
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
<!-- 配置分页助手插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
测试
@Test
public void test11() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper代理对象
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
//设置分页相关参数 当前页、每页显示条数
PageHelper.startPage(1,3);
List<Order> orders = mapper.selectAll();
// orders.forEach(System.out::println);
//获取分页相关参数
PageInfo<Order> orderPageInfo = new PageInfo<>(orders);
// System.out.println(orderPageInfo);
System.out.println("当前页:"+orderPageInfo.getPageNum());
System.out.println("每页显示条数:"+orderPageInfo.getPageSize());
System.out.println("总条数:"+orderPageInfo.getTotal());
System.out.println("总页数:"+orderPageInfo.getPages());
System.out.println("上一页:"+orderPageInfo.getPrePage());
System.out.println("下一页:"+orderPageInfo.getNextPage());
System.out.println("是否是第一页:"+orderPageInfo.isIsFirstPage());
sqlSession.close();
resourceAsStream.close();
}
2.7、mybatis多表查询
表与表的关系有:一对一、一对多、多对多。
2.7.1、一对一多表查询
一个订单只能对应一个用户
方法一:
<resultMap id="orderType" type="org.example.entity.Order">
<!-- 手动指定属性与字段对应关系
column:数据库字段
property:实体类的属性
-->
<id column="order_id" property="order_id"/>
<id column="create_time" property="create_time"/>
<id column="age" property="user.age"/>
<id column="id" property="user.id"/>
<id column="name" property="user.name"/>
</resultMap>
<select id="selectAll" resultMap="orderType">
select * from user_order uo,user where uo.uid=user.id
</select>
方法二:
<resultMap id="orderType" type="org.example.entity.Order">
<!-- 手动指定属性与字段对应关系
column:数据库字段
property:实体类的属性
-->
<id column="order_id" property="order_id"/>
<id column="create_time" property="create_time"/>
<!--
property:实体属性名称(private User user)
javaType:实体属性类型(org.example.entity.User)
-->
<association property="user" javaType="user">
<id property="id" column="id"/>
<id property="name" column="name"/>
<id property="age" column="age"/>
</association>
</resultMap>
<select id="selectAll" resultMap="orderType">
select * from user_order uo,user where uo.uid=user.id
</select>
2.7.2、一对多多表查询
一个用户有多个订单
<resultMap id="userMap" type="user">
<id column="id" property="id"/>
<id column="name" property="name"/>
<id column="age" property="age"/>
<!--
property:集合属性名称
ofType:集合数据类型
-->
<collection property="orderList" ofType="org.example.entity.Order">
<id column="order_id" property="order_id"/>
<id column="create_time" property="create_time"/>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select * from user_order uo,user where uo.uid=user.id
</select>
2.7.3、多对多多表查询
一个用户有多个权限,一个权限可以被对个用户拥有。
<resultMap id="userMap" type="user">
<id column="uid" property="id"/>
<id column="name" property="name"/>
<id column="age" property="age"/>
<collection property="roleList" ofType="org.example.entity.Role">
<id column="rid" property="id"/>
<id column="role_name" property="roleName"/>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select u.id uid,u.age,u.name,r.id rid,r.role_name from user u,role r,user_role ur where u.id = ur.uid and r.id=ur.rid
</select>
2.8、mybatis注解开发
代替映射文件
2.8.1、常用注解
- @lnsert:实现新增
- @Update:实现更新
- @Delete:实现删除
- @Select:实现查询
- @Result:实现结果集封装
- @Results:可以与@Result一起使用,封装多个结果集
- @One:实现一对一结果集封装
- @Many:实现一对多结果集封装
2.8.1、增删改查
public interface RoleMapper {
@Insert("insert into role values (#{id},#{roleName})")
void add(Role role);
@Delete("delete from role where id=#{id}")
void deleteById(int id);
@Update("update role set role_name=#{roleName} where id=#{id}")
void update(Role role);
@Results({
@Result(column = "id",property = "id"),
@Result(column = "role_name",property = "roleName")
})
@Select("select * from role")
List<Role> findAll();
}
<!-- 扫描映射注解 -->
<mappers>
<package name="org.example.dao"/>
</mappers>
测试
public class MybatisAnnoTest {
private RoleMapper roleMapper;
@Before
public void beforeTest() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
roleMapper = sqlSession.getMapper(RoleMapper.class);
}
@Test
public void addTest(){
Role role = new Role();
role.setRoleName("产品经理");
roleMapper.add(role);
}
@Test
public void deleteTest(){
roleMapper.deleteById(7);
}
@Test
public void updateTest(){
Role role = new Role();
role.setId(8);
role.setRoleName("广州部门产品经理");
roleMapper.update(role);
}
@Test
public void selectAllTest(){
List<Role> roles = roleMapper.findAll();
roles.forEach(System.out::println);
}
}
2.9、mybatis多表查询(注解方法)
表与表的关系有:一对一、一对多、多对多。
2.9.1、一对一多表查询
一个订单只能对应一个用户
方法一:
public interface OrderMapper {
@Results({
@Result(column = "order_id",property = "order_id"),
@Result(column = "create_time",property = "create_time"),
@Result(column = "age",property = "user.age"),
@Result(column = "id",property = "user.id"),
@Result(column = "name",property = "user.name")
})
@Select("select * from user_order uo,user where uo.uid=user.id")
List<Order> selectAll();
}
方法二:
@Select("select * from user where id=#{id}")
User findById(int id);
@Results({
@Result(column = "order_id",property = "order_id"),
@Result(column = "create_time",property = "create_time"),
@Result(
property = "user",//要封装的属性名称
column = "uid",//根据这个字段查询user表的数据
javaType = User.class, //要封装的实体类型
//select 代表查询这个借口的方法获取数据
one = @One(select = "org.example.dao.UserMapper.findById")
)
})
@Select("select * from user_order")
List<Order> selectAll();
2.9.2、一对多多表查询
一个用户有多个订单
@Select("select * from user_order where uid=#{uid}")
List<Order> findOrderByUid(int uid);
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(column = "age",property = "age"),
@Result(
property = "orderList",
column = "id",
javaType = List.class,
many = @Many(select = "org.example.dao.OrderMapper.findOrderByUid")
)
})
@Select("select * from user")
List<User> findAll();
2.9.3、多对多多表查询
一个用户有多个权限,一个权限可以被对个用户拥有。
@Results({
@Result(column = "id",property = "id"),
@Result(column = "role_name",property = "roleName"),
})
@Select("select * from user_role ur,role r where ur.rid=r.id and ur.uid=#{uid}")
List<Role> findRolesByUid(int uid);
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(column = "age",property = "age"),
@Result(
property = "roleList",
column = "id",
javaType = List.class,
many = @Many(select = "org.example.dao.RoleMapper.findRolesByUid")
)
})
@Select("select * from user")
List<User> findUserRole();
2.10、spring整合mybatis
导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
在applicationContext.xml中配置
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:SqlMapConfig-spring.xml"/>
</bean>
<!-- 扫描mapper所在的包 为mapper创建实现类 替代SqlMapConfig.xml的mapper标签-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.example.dao"/>
</bean>
SqlMapConfig-spring.xml
<?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>
<!-- 定义别名 -->
<typeAliases>
<typeAlias type="org.example.entity.User" alias="user"/>
</typeAliases>
<!-- 自定义类型处理器 -->
<typeHandlers>
<typeHandler handler="org.example.handler.DateTypeHandler"/>
</typeHandlers>
<!-- 配置分页助手插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
</configuration>
测试(模拟service层)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringMybatisTest {
@Autowired
private UserMapper userMapper;
@Test
public void findUserRoleTest(){
List<User> userRole = userMapper.findUserRole();
userRole.forEach(System.out::println);
}
}
2.11、spring boot整合mybatis
2.11.1、简单演示
导入数据库连接依赖和mybatis的启动依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
在application.yml配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/jdbc_template_test?serverTimezone=Asia/Shanghai
password: 553057712
username: root
创建dao接口
@Mapper //相当于mapper标签,反射创建实现类
public interface UserMapper {
@Select("select * from user")
List<User> findAllUser();
}
测试
@SpringBootTest
class SpringBootDemoApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void findAllUserTest() {
List<User> allUser = userMapper.findAllUser();
allUser.forEach(System.out::println);
}
}
mybatis: configuration: map-underscore-to-camel-case: true开启实体与表字段映射规则为:userName-->user_name
3、mybatis plus
mybatis的增强版
官网:MyBatis-Plus
3.1、标准数据层CRUD功能
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
//添加
@Test
public void addTest(){
Role role = new Role();
role.setRoleName("测试工程师");
roleDao.insert(role);
}
//更新
@Test
public void updateTest(){
Role role = new Role();
role.setId(2);
role.setRoleName("项目经理");
roleDao.updateById(role);
}
//查询全部
@Test
public void selectAllTest(){
List<User> users = userDao.selectList(null);
users.forEach(System.out::println);
}
//查询一个
@Test
public void selectByIdTest(){
Role role = roleDao.selectById(2);
System.out.println(role);
}
3.2、分页查询
配置分页拦截器
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//定义mp拦截器
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//添加具体的拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
测试
//分页查询
@Test
public void selectByPage(){
IPage page = new Page(1,2);
roleDao.selectPage(page, null);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示数:"+page.getSize());
System.out.println("总条数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("数据:"+page.getRecords());
}
3.3、条件查询
//条件查询
@Test
public void selectByConditionTest(){
// //方式一
// QueryWrapper qw = new QueryWrapper();
// //小于5
// qw.lt("id",5);
// List<Role> roleList = roleDao.selectList(qw);
// System.out.println(roleList);
// //方式二:lambda
// QueryWrapper<Role> qw = new QueryWrapper<>();
// //小于5
// qw.lambda().lt(Role::getId,5);
// List<Role> roleList = roleDao.selectList(qw);
// System.out.println(roleList);
// //方式三:lambda
// LambdaQueryWrapper<Role> lqw = new LambdaQueryWrapper<>();
// //小于5
// lqw.lt(Role::getId,5);
// List<Role> roleList = roleDao.selectList(lqw);
// System.out.println(roleList);
//多条件
LambdaQueryWrapper<Role> lqw = new LambdaQueryWrapper<>();
//小于5大于2
// lqw.lt(Role::getId,5).gt(Role::getId,2);
//小于2或大于5
lqw.lt(Role::getId,2).or().gt(Role::getId,5);
List<Role> roleList = roleDao.selectList(lqw);
System.out.println(roleList);
}
3.4、查询投影
只查我想要的参数,其他为默认值
//查询投影
// LambdaQueryWrapper<Role> lqw = new LambdaQueryWrapper<>();
// lqw.select(Role::getRoleName);
QueryWrapper<Role> qw = new QueryWrapper<>();
qw.select("role_name");
List<Role> roleList = roleDao.selectList(qw);
System.out.println(roleList);
//SELECT count(*) as count,role_name FROM role GROUP BY role_name
QueryWrapper<Role> qw = new QueryWrapper<>();
qw.select("count(*) as count,role_name");//统计数
qw.groupBy("role_name");//分组
List<Map<String, Object>> maps = roleDao.selectMaps(qw);
System.out.println(maps);
3.5、字段映射与表名映射
@TableField 字段映射
属性
- value:设置数据库表字段名称
- exist:设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用
- select:设置属性是否参与查询,此属性与select()映射配置不冲突
@TableName 表名映射
@TableName("role")
public class Role {
private int id;
@TableField(value = "role_name",select = false)
private String roleName;
@TableField(exist = false)
private String dec;
3.6、id生成策略
@TableId(type = IdType.AUTO)
private int id;
全局配置id生成策略
mybatis-plus:
global-config:
db-config:
id-type: auto
3.7、逻辑删除
例如:一个员工从公司离职,一般不会把员工信息从数据库中物理删除,而是设置一个标志表示离职。
当删除时执行的是更新操作,UPDATE role SET deleted=1 WHERE id=? AND deleted=0
@TableName("role")
@Data
public class Role {
private int id;
private String roleName;
@TableLogic(value = "0",delval = "1")
private int deleted;
}
当查询时会排除deleted为1的数据
SELECT id,deleted FROM role WHERE id=? AND deleted=0
全局配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
3.8、乐观锁
在秒杀应用中会遇到这样的问题:
前提:秒杀商品为0时停止秒杀
假设:秒杀商品剩余一个 ,但有多个用户同时抢这一个商品(并发都过了剩余商品大于0的判断),会造成剩余商品小于0的问题
解决办法:加乐观锁
@Data
public class Role {
@Version
private int version;
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//定义mp拦截器
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//添加乐观锁拦截器
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
测试
UPDATE role SET role_name=?, version=? WHERE id=? AND version=? AND deleted=0
@Test
public void updateTest(){
Role roleBefore = roleDao.selectById(4);
Role role = new Role();
role.setId(4);
role.setVersion(roleBefore.getVersion());
role.setRoleName("项目经理");
roleDao.updateById(role);
}