一、连接池
- 连接池的概念:将JDBC的操作进行了封装,合理的管理连接资源,在启动的时候会直接创建连接的对象,需要使用操作数据库的时候,直接拿着已经开启的连接使用即可,可以避免频繁的链接数据库操作,减少IO(磁盘IO和网络IO)的操作,以此减少数据库的连接压力
1.1 C3P0连接池
- 需要导入C3P0的依赖包:数据库的驱动包、C3P0的核心包和C3P0的依赖包
- C3P0的c3p0-config.xml配置文件,要求:文件名称不能修改,并且文件必须放在src目录下
<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
<default-config>
<!--数据库连接必要的四个参数-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db_bank?useUnicode=true&characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password">root</property>
<!--连接池其他参数的配置-->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<mysql>
<!--数据库连接必要的四个参数-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db_bank?useUnicode=true&characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password">root</property>
<!--连接池其他参数的配置-->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</mysql>
</c3p0-config>
- 用C3P0连接池封装的工具类
public class C3P0Utils {
//声明一个数据源对象
private static DataSource dataSource = new ComboPooledDataSource("mysql");
//获取连接
public static Connection getConn(){
try {
return dataSource.getConnection();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//关闭资源
public static void close(Object...args){
try {
if(args.length==3 && args[2] != null){
ResultSet rs = (ResultSet) args[2];
rs.close();
}
if(args[1] != null){
PreparedStatement ps =(PreparedStatement)args[1];
ps.close();
}
if(args[0] != null){
Connection conn = (Connection)args[0];
conn.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
1.2 Druid连接池
- 依赖的jar包:数据库的驱动包、druid的核心包
创建一个properties文件,名字自定义即可
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db_bank?useUnicode=true&characterEncoding=utf-8
username=root
password=root
创建Druid工具类
public class DruidUtils {
//声明一个数据源对象
private static DataSource dataSource;
//使用静态代码块实现加载properties文件
static{
try {
//获取properties文件
InputStream in = DruidUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
//将properties对象交给连接池工厂,由工厂创建相应的数据源对象
dataSource = DruidDataSourceFactory.createDataSource(prop);
}catch (Exception e){
e.printStackTrace();
}
}
//创建连接
public static Connection getConn(){
try {
return dataSource.getConnection();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//关闭资源
public static void close(Object...args){
try {
if(args.length==3 && args[2] != null){
ResultSet rs = (ResultSet) args[2];
rs.close();
}
if(args[1] != null){
PreparedStatement ps =(PreparedStatement)args[1];
ps.close();
}
if(args[0] != null){
Connection conn = (Connection)args[0];
conn.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
二、Mybatis概念
- Mybatis是一个ORM框架,主要用来Java和数据库操作之间的数据交互,前生ibatis,如果不用Mybatis,操作数据库要么使用原生的JDBC,要么使用连接池。
2.1 原生JDBC中存在的问题
- 问题1:sql语句是直接写在了Java代码中,这种写法称为硬编码
解决:使用mybatis之后,将Java代码和sql语句进行分离,将所有的sql语句抽取出来,配置在xml文件中即可 - 问题2:如果需要向数据库sql语句中拼接条件,需要使用?占位符,仍然属于硬编码
解决:使用mybatis之后,sql语句都在xml文件中,可以使用mybatis提供的输入映射配置,可以自动的将条件和需要的值填充 - 问题3:在原生的jdbc中,结果集需要对ResultSet进行遍历,并且依次拿到里面的值,再set到对应的模型中,及其麻烦的硬编码模式
解决:使用mybaits之后,在执行完sql语句之后,通过输出映射,可以自动的将所有的数据直接填充到模型中
2.2 Mybatis的结构图
2.3 Mybatis的核心组件
- 会话工厂构造器:SqlSessionFactoryBuilder,用来读取核心配置文件并生成工厂对象
- 会话工厂:SqlSessionFactory,作用:用来开启会话
- 会话:SqlSession,用来获取sql语句,并让执行器执行sql语句,拿到结果集
- Mapper代理:sql Mapper代理对象
三、Mybatis入门
3.1 需要依赖的jar包
- 需要数据库的驱动包
- 需要mybatis的核心包
- 需要mybatis的依赖包(工具包和日志包log4j)
3.2 开发Mybatis入门代码
编写核心配置文件:mybatis-config.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">
<!--给mybatis配置事务管理方式-->
<transactionManager type="JDBC" />
<!--配置数据源需要的配置数据-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/db_bank?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--加载单个mapper文件-->
<mappers>
<mapper resource=""/>
</mappers>
</configuration>
创建POJO类:目前要求POJO类的属性必须和数据库中字段的属性一致
public class User {
private int user_id;
private String user_name;
private double money;
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "User{" +
"user_id=" + user_id +
", user_name='" + user_name + '\'' +
", money=" + money +
'}';
}
}
编写单个的Mapper.xml文件,当写好配置文件之后,一定记得到核心配置文件中加载mapper.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
namespace:给当前的mapper取一个唯一的名字,用来区别,目前可以自定义
在Mapper代理方式之后,namespace有特殊的含义
-->
<mapper namespace="userMapper">
<!--需求:根据id值查询用户的数据,结果集只有一条
属性:
id:给该select标签取一个唯一的标识,目前可以自定义
parameterType:输入映射,填写的是输入参数的数据类型
resultType:输出映射,填写的是输出映射的数据类型,如果是返回的模型对象,输出映射
填写对应模型的全路径名
#{value}:用来后续将输入映射的实际参数值,填充在这个位置,相当于?
-->
<select id="queryUserById" parameterType="java.lang.Integer" resultType="com.woniuxy.model.User">
<!--编写sql语句-->
select user_id,user_name,money from t_user where user_id = #{value}
</select>
</mapper>
编写测试类
public class Test {
public static void main(String[] args) throws Exception{
//获取核心配置文件,获取的是输入流
String path = "mybatis-config.xml";
InputStream config = Resources.getResourceAsStream(path);
//获取会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
/*执行sql语句
第一个参数:用来关联执行哪一条sql语句,namespace值.id值
第二个参数:sql语句需要的实际参数值
*/
User user = sqlSession.selectOne("userMapper.queryUserById",1);
System.out.println(user);
}
}
3.3 Mybatis的其他其他操作
- 实现模糊查询
需求:查询用户名以“小”开头的用户数据
1)User的POJO类已经创建好了
2)全局配置文件mybatis-config.xml创建好了,不需要再次配置(如果切换了数据库名需要修改URL,如果有新的xxxMapper。xml文件,需要再次加载,在mappers标签里面再增加一个mapper标签)
3)再UserMapper.xml配置文件中新增查询标签
4)测试代码
重点:#{value}和${value}<!-- 需求:根据用户名查询出以"小"开头的用户数据 parameterType:输入映射,输入映射String resultType:输出映射,输出多个User对象,仍然是全路径 --> <select id="queryUserrByName" parameterType="java.lang.String" resultType="com.woniuxy.model.User"> select user_id,user_name,money from t_user where user_name like "${value}%" </select>
public class LikeTest {
public static void main(String[] args) throws Exception{
//获取工厂对象
String path = "mybatis-config.xml";
InputStream config = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
/*执行sql语句并获取结果集
第一个参数:找到指定的select标签,select标签是sql语句,namespace.id
*/
List<User> userList =
sqlSession.selectList("userMapper.queryUserByName","小");
System.out.println(userList);
}
}
1、#{value}:标识占位符替换,不是字符串,大括号中的值可以任意,一般推荐和POJO中的属性名称一致
2、${value}:用字符串进行拼接,会有sql注入的危险,一般不推荐使用,大括号中的值只能是value
四、Mybatis的增删改查(MVC开发)
4.1 搭建环境
引入依赖的jar包,包含数据库驱动包,mybatis核心包和mybatis依赖包
- 创建全局配置文件,全局配置文件名称SqlMapConfig.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"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/db_book?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--如果有单个新Mapper.xml文件产生,则引入一个-->
</mappers>
</configuration>
- 配置日志文件,日志文件使用log4j,和mybatis的核心配置文件在同一个目录下,并且名字为log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- 创建类(model、POJO、domain)
4.2 Mybatis查询操作
- 需求:查询所有的书籍信息
创建一个BookDao接口
public interface BookDao {
//定义一个查询所有书籍数据的方法
public List<Book> queryAllBook() throws Exception;
}
创建BookMapper.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="bookMapper">
<!--
需求:查询所有的书籍
parameterType:输入映射,无
resultType:输出映射,Book的实体对象
-->
<select id="queryBook" resultType="com.woniuxy.model.Book">
select book_id,book_name,price,author,number from t_book
</select>
</mapper>
创建BookDao的实现类,需要查询到所有的数据
public class BookDaoImpl implements BookDao {
/*
需要操作数据库
*/
@Override
public List<Book> queryAllBook() throws Exception {
//获取工厂
String path = "SqlMapConfig.xml";
InputStream config = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
//执行
List<Book> bookList = sqlSession.selectList("bookMapper.queryBook");
//关闭会话
sqlSession.close();
return bookList;
}
}
创建Book的service接口
public interface BookService {
//查询所有的书籍
public List<Book> queryAllBook() throws Exception;
}
创建Book的service实现类
public class BookServiceImpl implements BookService {
@Override
public List<Book> queryAllBook() throws Exception {
//调用dao中的方法
BookDao bookDao = new BookDaoImpl();
List<Book> bookList = bookDao.queryAllBook();
return bookList;
}
}
创建Book的controller类
public class BookController {
public static void main(String[] args) {
new BookController().queryAllBook();
}
public void queryAllBook(){
try {
//创建Service对象
BookService bookService = new BookServiceImpl();
List<Book> bookList = bookService.queryAllBook();
System.out.println(bookList);
}catch (Exception e){
e.printStackTrace();
}
}
}
4.3 Mybatis新增操作
- 需求:向book表中新增一条数据
编写dao接口中的方法
//新增数据,将book对象传入
public int addBook(Book book) throws Exception;
编写Mapper.xml文件
<!--
需求:新增一条book的数据
parameterType:输入映射,book对象
resultType:输出映射,无
-->
<insert id="insertBook" parameterType="com.woniuxy.model.Book">
insert into t_book(book_name, price, author, number)
value (#{book_name},#{price},#{author},#{number})
</insert>
编写dao的实现类
@Override
public int addBook(Book book) throws Exception {
//创建工厂
String path = "SqlMapConfig.xml";
InputStream config = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
//执行方法,返回值标识数据库中修改的行数,新增标识新增的行数
int row = sqlSession.insert("bookMapper.insertBook",book);
System.out.println(row);
//提交事务
sqlSession.commit();
//关闭会话
sqlSession.close();
return row;
}
编写Service接口
//新增一条book数据
public int addBook(Book book) throws Exception;
编写service实现类
@Override
public int addBook(Book book) throws Exception {
//调用dao中的方法
BookDao bookDao = new BookDaoImpl();
int row = bookDao.addBook(book);
return row;
}
编写controller中的方法
//新增book数据
public void addBook(){
try {
Scanner in = new Scanner(System.in);
Book book = new Book();
//获取书籍的数据
System.out.print("请输入书籍名称");
String book_name = in.next();
System.out.print("请输入单价:");
double price = in.nextDouble();
System.out.print("请输入作者:");
String author = in.next();
System.out.print("请输入数量:");
int number = in.nextInt();
//将数据封装到book对象中
book.setAuthor(author);
book.setBook_name(book_name);
book.setNumber(number);
book.setPrice(price);
//创建Service对象
BookService bookService = new BookServiceImpl();
int row = bookService.addBook(book);
if(row > 0){
System.out.println("新增成功");
}else{
System.out.println("新增失败");
}
}catch (Exception e){
e.printStackTrace();
}
}
在实际开发中,经常需要在新增数据之后,立即获取当前新增数据的主键id值。如果使用了selectKey标签,mybatis会将主键的值映射到传入的对象中,在dao中获取的时候,直接用传入的对象.get主键属性();
<!--
需求:新增一条book的数据
parameterType:输入映射,book对象
resultType:输出映射,无
-->
<insert id="insertBook" parameterType="com.woniuxy.model.Book">
<!--在新增之后,立即获取刚刚新增数据的主键
keyColumn:指定数据库中作为主键的字段名称
keyProperty:在pojo模型中代表主键的属性名称
resultType:主键对应的数据类型
-->
<selectKey keyColumn="book_id" order="AFTER"
keyProperty="book_id" resultType="java.lang.Integer"> select last_insert_id()
</selectKey>
insert into t_book(book_name, price, author, number)
value (#{book_name},#{price},#{author},#{number})
</insert>
4.4 Mybatis修改操作
- 需求:根据id值修改指定书籍的单价
编写dao的接口
//根据书籍的id值,修改单价
public int updateBook(Book book) throws Exception;
编写mapper.xml配置文件
<!--
需求:根据id值,修改书籍的单价
parameterType:输入映射,输入book对象
resultType:输出映射,没有
-->
<update id="editBookById" parameterType="com.woniuxy.model.Book"> update t_book set price = #{price} where book_id = #{book_id} </update>
编写dao的实现类
@Override
public int updateBook(Book book) throws Exception {
//获取工厂
String path = "SqlMapConfig.xml";
InputStream config = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
//执行sql语句
int row = sqlSession.update("bookMapper.editBookById",book);
//提交会话
sqlSession.commit();
//关闭会话
sqlSession.close();
return row;
}
编写service接口
//根据书籍的id值修改书籍的单价
public int updateBookById(Book book) throws Exception;
编写service的实现类
@Override
public int updateBookById(Book book) throws Exception {
//调用dao中的方法
BookDao bookDao = new BookDaoImpl();
int row = bookDao.updateBook(book);
return row;
}
编写controller中的方法
//修改书籍的单价
public void updateBook() throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("请输入书籍编号:");
int book_id = in.nextInt();
System.out.print("请输入书籍的单价:");
double price = in.nextDouble();
Book book = new Book();
book.setBook_id(book_id);
book.setPrice(price);
//创建Service对象
BookService bookService = new BookServiceImpl();
int row = bookService.updateBookById(book);
if(row > 0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
}
4,5 Mybatis删除操作
- 根据用户的id值删除用户数据
创建UserDao的接口
public interface UserDao {
//根据id值删除用户数据
public int deleteById(int user_id) throws Exception;
}
创建UserMapper.xml配置文件,因为是新建的mapper.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">
<!--
需求:根据用户的id值,删除用户的数据
parameterType:输入映射,输入用户id值
resultType:输出映射,无
-->
<delete id="deleteUser" parameterType="int">
delete from t_user where user_id=#{user_id}
</delete>
</mapper>
创建UserDao的实现类
public class UserDaoImpl implements UserDao {
@Override
public int deleteById(int user_id) throws Exception {
//获取工厂
String path = "SqlMapConfig.xml";
InputStream config = Resources.getResourceAsStream(path);
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(config);
//开启会话
SqlSession sqlSession = factory.openSession();
//执行方法
int row = sqlSession.delete("userMapper.deleteUser",1);
//提交事务
sqlSession.commit();
//关闭会话
sqlSession.close();
return row;
}
}
创建User的业务层接口
//根据id值删除用户
public int deleteUser(int user_id) throws Exception;
创建User的业务层实现类
public class UserServiceImpl implements UserService {
@Override
public int deleteUser(int user_id) throws Exception {
//调用dao中的方法
UserDao userDao = new UserDaoImpl();
int row = userDao.deleteById(user_id);
return row;
}
}
编写controller
public void deleteUser() throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("请输入要删除的编号:");
int user_id = in.nextInt();
UserService userService = new UserServiceImpl();
int row = userService.deleteUser(user_id);
if(row > 0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
4.6 Junit单元测试
- 需要单独执行的方法,可以使用Junit单元测试,让方法可以脱离主方法直接执行,要求该方法不能有形式参数,要求该方法不能是静态的。
- Junit一般使用的注解有三个
@Test:要单独执行的方法上添加的注解
@Before:在单独执行的方法之前就要执行的注解
@After:在单独执行的方法之后需要执行的注解 - 事例代码
public class JunitTest {
@Test
public void test(){
System.out.println("单元测试的方法");
}
@Before
public void before(){
System.out.println("之前的方法");
}
@After
public void after(){
System.out.println("之后执行");
}
}
五、Mapper代理方式
- 新增一个Mapper接口,用Mapper接口替代以前的Dao接口,dao的实现类可以省略,mybatis会自动地完成dao实现类中的方法执行。
- 如果要使用Mapper代理方式实现代码执行,需要遵循以下规则:
1、namespace的值必须式对应的Mapper接口的全路径名
2、id:必须和mapper接口中定义方法的方法名称一致
3、parameterType:必须和mapper接口中定义方法的形式参数数据类型一致
4、resultType:必须和mapper接口中定义的方法的返回值数据类型一致
以根据id值查询用户数据为例:
编写Mapper接口
public interface UserMapper {
//根据用户id查询用户的值
public User queryUserById(int user_id) throws Exception;
}
编写Mapper.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="com.woniuxy.mapper.UserMapper">
<!--查询所有的用户
parameterType:输入映射,用户的id值
resultType:输出映射,用户对象
public User queryAllUser(int user_id) throws Exception;
如果使用mapper代理方式实现,需要遵循以下规则:
id:必须和mapper接口中定义方法的方法名称一致
parameterType:必须和mapper接口中定义方法的形式参数数据类型一致
resultType:必须和mapper接口中定义的方法的返回值数据类型一致
-->
<select id="queryUserById" parameterType="int" resultType="com.woniuxy.model.User">
select user_id,user_name,tel,password from t_user where user_id = #{user_id}
</select>
</mapper>
编写Service接口
public interface UserService {
//根据id查询用户数据
public User queryUserById(int user_id) throws Exception;}
编写Service实现类
public class UserServiceImpl implements UserService {
@Override
public User queryUserById(int user_id) throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取mapper对象
UserMapper userMapper =
sqlSession.getMapper(UserMapper.class);
//执行接口中的方法
User user = userMapper.queryUserById(user_id);
//关闭会话
sqlSession.close();
return user;
}
}
编写controller
public class UserController {
@Test
public void queryUserById() throws Exception{
UserService userService = new UserServiceImpl();
User user = userService.queryUserById(2);
System.out.println(user);
}
}
六、Mybatis的配置
6.1 Mybatis配置文件中读取properties文件
在源目录下创建db.properties配置文件,里面用于存放连接数据库的核心数据
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db_book?useUnicode=true&characterEncoding=utf-8
username=root
password=root
在Mybatis核心配置文件中,加载db.properties,并且将里面的四个参数读取到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>
<!--读取properties配置文件-->
<properties resource="db.properties" />
<!--配置环境-->
<environments default="development">
<environment id="development">
<!--数据库的事务管理-->
<transactionManager type="JDBC"/>
<!--数据源-->
<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>
<mappers>
<!--如果有单个新Mapper.xml文件产生,则引入一个-->
<mapper resource="mapper/BookMapper.xml"></mapper>
</mappers>
</configuration>
6.2 设置别名
- 输入映射和输出映射的时候,每次都需要写全路径,给每一个路径取一个别名,之后再使用该类作为输入映射和输出映射的时候,可以直接使用别名即可。
- 设置别名是在mybatis的核心配置文件中设置
方式1:每次给单个模型类设置别名,比较麻烦
方式2:使用包扫描的方式实现别名设置,推荐方式<!--设置别名-->
<typeAliases>
<!--
type:表示要设置别名的全路径
alias:给该路径取得别名
-->
<typeAlias type="com.woniuxy.model.Book" alias="book" />
<typeAlias type="com.woniuxy.model.User" alias="user" />
</typeAliases>
<typeAliases>
<!--使用包扫描的方式:要求所有的模型类需要在同一个包下
如果使用包扫描,那么别名默认为类名,可以是类名的小驼峰
比如:类名Book 别名:Book或者book
类名:UserBook 别名:UserBook 或者userBook
-->
<package name="com.woniuxy.model"/>
</typeAliases>
6.3 使用mapper的包扫描
- 如果开发中使用的是Mapper代理方式,那么可以在mybatis的核心配置文件中,在加载mapper的时候,可以使用包扫描的方式
- 如果使用包扫描,需要遵循两个规则
规则1:mapper接口和mapper.xml必须放在同一个包下
规则2:mapper接口名称和mapper.xml名称必须一致
<mappers>
<package name = "com.woniuxy.mapper"></package>
</mappers>
七、输入映射和输出映射
7.1 输入映射
- 输入映射有两种方式:parameterType和parameterMap,如果只有一个参数,写上该参数的数据类型即可,如果需要多个参数,可以选择先将数据封装到对象中,将对象作为输入映射即可。
7.2 输出映射
- 输出映射有两种方式:resultType和resultMap
- resultType输出映射,可以输出一个对象的属性,会将数据直接封装到对象中,resultType输出映射的值是简单类型,要求查询的数据库结果集必须是一行一列的结果集。
- resultMap输出映射,resultMap用在复杂的查询语句里面,它不能作为简单的映射,需要自定义一个resultMap标签,并且对每一个数据做逐一的映射关系
需求:查询所有的书籍数据,用resultMap方式进行输出映射
<!--
需求:查询所有的书籍信息,要求使用resultMap输出映射
public List<Book> queryBook() throws Exception;
-->
<!--自定义resultMap标签
id:表示给给resultMap取一个唯一的标识符
type:表示该结果集要输出到哪一个对象对应的模型类全路径,可以使用别名
resultMap标签和select标签输出映射关联是通过id值和resultMap值一致进行关联
-->
<resultMap id="queryBookMap" type="Book">
<!--
id标签:用来映射数据库中主键和属性中代表主键的属性名称映射关系
column:数据库中代表主键的字段名称
property:模型类中用来代表主键的属性名称
-->
<id column="book_id" property="book_id" />
<!--
result标签:用来映射数据库中除了主键以外的其他所有字段
column:数据库中代表除了主键主键以外的字段名称
property:模型类中用来代表除了主键以外的属性名称
-->
<result column="book_name" property="book_name" />
<result column="price" property="price" />
<result column="author" property="author" />
<result column="number" property="number" />
</resultMap>
<select id="queryBook" resultMap="queryBookMap">
select book_id,book_name,price,author,number from t_book
</select>
总结:在实际开发中,单表查询建议使用resultType输出映射,只要是多表查询,建议使用resultMap,实际开发中,一般情况resultMap居多。
- resultType不能支持延迟加载,而resultMap可以支持延迟加载
八、多表查询
8.1 一对一关系映射
- 需求:根据用户的id查询用户数据及用户信息数据
- 输出映射使用resultType方式进行:输出映射必须是同一个模型类
需要对模型类进行重构
//省略了get和set方法,自己添加
public class UserCustom extends User{
private int infoId;
private int userId;
private String realName;
private String address;
private int height;
private double weight;
}
编写mapper接口
public interface UserMapper {
//根据id值查询用户数据及用户信息数据
public UserCustom queryUserAndInfoById(int userId) throws Exception;
}
编写mapper.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="com.woniuxy.mapper.UserMapper">
<!--
需求:根据id查询用户数据及用户信息数据
public UserCustom queryUserAndInfoById(int userId) throws Exception;
-->
<select id="queryUserAndInfoById" parameterType="int" resultType="UserCustom">
select u.user_id,user_name,tel,password,info_id,real_name,address,height,weight
from
db_book.t_user as u join t_info ti
on
u.user_id = ti.user_id
where
u.user_id = #{user_id}
</select>
</mapper>
编写service接口
public interface UserService {
//查询用户及用户信心数据
public UserCustom queryUserAndInfoById(int userId) throws Exception;
}
编写service的实现类
public class UserServiceImpl implements UserService {
@Override
public UserCustom queryUserAndInfoById(int userId) throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取mapper对象
UserMapper userMapper =
sqlSession.getMapper(UserMapper.class);
UserCustom userCustom =
userMapper.queryUserAndInfoById(userId);
//关闭会话
sqlSession.close();
return userCustom;
}
}
编写controller代码
public class UserController {
@Test
public void queryUserAndInfoById()throws Exception{
//获取service对象
UserService userService = new UserServiceImpl();
UserCustom userCustom =
userService.queryUserAndInfoById(2);
System.out.println(userCustom);
}
}
- 映射输出使用resultMap方式(还是一对一)
需要找到主表和附表,主表:t_user表,附表:t_info表
重构pojo的模型类:重构主表的pojo类
//省略了get和set方法
public class User {
private int userId;
private String userName;
private String password;
private String tel;
//在这里声明一个info对象
private Info info;
}
编写mapper接口
//根据id值查询用户及用户信息的数据
public User queryUserAndInfoById2(int userId) throws Exception;
编写mapper.xml配置文件,输出映射使用resultMap
<!--
需求:根据id查询用户数据及用户信息数据
public User queryUserAndInfoById2(int userId) throws Exception;
-->
<!--自定义resultMap
多表查询的时候,type表示映射的主表对应的pojo类的全路径,可以使用别名 -->
<resultMap id="queryUserAndInfoById2Map" type="User">
<!--多表查询(这里是一对一),首先关联主表和pojo类的映射关系-->
<!--
id标签:表示主表中用主键和主键对应的pojo类中属性的关联映射
column:数据库中主表代表主键的字段
property:属性中代表主键的属性名称
-->
<id column="user_id" property="userId"/>
<!--主表中的其他字段,用result标签进行关联映射
column:数据库中对应的字段名称
property:属性中对应的属性名称
-->
<result column="user_name" property="userName" />
<result column="tel" property="tel" />
<result column="password" property="password" />
<!--关联附表中的属性和字段映射,因为是一对一的关系映射,因此使用标签association
property:主表对应的pojo类中声明的附表pojo类对象简单User类中的info对象 javaType:附表对应的pojo类全路径,可以适应别名,info对象对应的全路径-->
<association property="info" javaType="Info">
<!--重复主表中的操作,分别将主键和普通字段与pojo类的属性进行关联映射-->
<id column="info_id" property="infoId" />
<result column="user_id" property="userId" />
<result column="real_name" property="realName" />
<result column="address" property="address" />
<result column="height" property="height" />
<result column="weight" property="weight" />
</association>
</resultMap>
<select id="queryUserAndInfoById2" parameterType="int" resultMap="queryUserAndInfoById2Map">
select u.user_id,user_name,tel,password,info_id,real_name,address,height,weight
from
db_book.t_user as u join t_info ti
on
u.user_id = ti.user_id
where
u.user_id = #{user_id}
</select>
编写service接口
//查询用户及用户信息数据
public User queryUserAndInfoById2(int userId) throws Exception;
编写service实现类
@Override
public User queryUserAndInfoById2(int userId) throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取mapper对象
UserMapper userMapper =
sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserAndInfoById2(userId);
//关闭会话
sqlSession.close();
return user;
}
编写conotroller中的代码
@Test
public void queryUserAndInfoById2()throws Exception{
//获取service对象
UserService userService = new UserServiceImpl();
User user = userService.queryUserAndInfoById2(2);
System.out.println(user);
}
8.2 一对多关系映射
- 需求:根据用户id值,查询用户数据及用户对应的订单数据
- 主表:用户表,附表:订单表
对User表进行重构
//这里省略了get和set方法
public class User {
private int userId;
private String userName;
private String password;
private String tel;
//在这里声明一个Order对象的集合
private List<Order> orderList;
}
编写mapper接口
//根据用户的id值,查询用户数据及用户购买的订单数据
public User queryUserAndOrderById(int userId) throws Exception;
编写mapper.xml配置文件
<!--
需求:根据用户的id值,查询用户数据及用户购买的订单数据
public User queryUserAndOrderById(int userId) throws Exception;
-->
<!--自定义resultMap-->
<resultMap id="queryUserAndOrderByIdMap" type="User">
<!--先映射主表关联-->
<id column="user_id" property="userId" />
<result column="user_name" property="userName"/>
<result column="tel" property="tel"/>
<result column="password" property="password"/>
<!--关联附表的映射关系,这里是一对多的关系,collection标签-->
<collection property="orderList" ofType="Order">
<id column="order_id" property="orderId" />
<result column="user_id" property="userId" />
<result column="create_time" property="createTime" />
<result column="note" property="note" />
</collection>
</resultMap>
<select id="queryUserAndOrderById" parameterType="int" resultMap="queryUserAndOrderByIdMap">
select tu.user_id,user_name,password,tel,order_id,create_time,note from
db_book.t_user as tu join t_order t
on
tu.user_id = t.user_id
where
tu.user_id = #{userId}
</select>
编写service接口
//根据id值查询用户及用户对应的订单数据
public User queryUserAndOrderById(int userId) throws Exception;
编写service的实现类
@Override
public User queryUserAndOrderById(int userId) throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取mapper对象
UserMapper userMapper =
sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserAndOrderById(userId);
//关闭会话
sqlSession.close();
return user;
}
编写controller
@Test
public void queryUserAndOrderById()throws Exception{
//获取service对象
UserService userService = new UserServiceImpl();
User user = userService.queryUserAndOrderById(2);
System.out.println(user);
}
8.3 多对多关系映射
- 需求:查询出所有用户购买的所有书籍
- 主表:用户表,附表:订单表,订单明细表,书籍表
对pojo类进行重构
1、重构User类
public class User {
private int userId;
private String userName;
private String password;
private String tel;
//需要通过User类找到Order类,声明成order对象的集合
private List<Order> orderList;
}
2、重构Order类
public class Order {
private int orderId;
private int userId;
private String createTime;
private String note;
//声明订单明细,声明detail对象的集合
private List<Detail> detailList;
}
3、重构detail类
public class Detail {
private int detailId;
private int orderId;
private int bookId;
//声明一个book对象,
private Book book;
}
编写mapper接口
//查询所有用户的购买的所有商品
public List<User> queryUserAndBook() throws Exception;
编写Mapper.xml配置文件
<!--
需求:查询所有用户的购买的所有商品
public List<User> queryUserAndBook() throws Exception;
-->
<!--自定义resultMap-->
<resultMap id="queryUserAndBookMap" type="User">
<!--关联映射主表user的关系-->
<id column="user_id" property="userId" />
<result column="user_name" property="userName" />
<result column="tel" property="tel" />
<result column="password" property="password" />
<!--关联映射user的模型类中的orderList映射,一对多-->
<collection property="orderList" ofType="Order">
<id column="order_id" property="orderId" />
<result column="user_id" property="userId" />
<result column="create_time" property="createTime" />
<result column="note" property="note"/>
<!--关联映射order类中的detailList映射,一对多-->
<collection property="detailList" ofType="Detail">
<id column="detail_id" property="detailId" />
<result column="order_id" property="orderId" />
<result column="book_id" property="bookId" />
<!--关联映射detail中的book对象,一对一-->
<association property="book" javaType="Book"> <id column="book_id" property="bookId" /> <result column="book_name" property="bookName" />
<result column="price" property="price" /> <result column="number" property="number" /> <result column="author" property="author" /> </association>
</collection>
</collection>
</resultMap>
<select id="queryUserAndBook" resultMap="queryUserAndBookMap"> select
tu.user_id,user_name,tel,password, t.order_id,create_time, note,detail_id,tb.book_id,book_name,price,author,number from
db_book.t_user tu
join
t_order t on tu.user_id = t.user_id
join
t_detail td on t.order_id = td.order_id
join
t_book tb on td.book_id = tb.book_id ;
</select>
编写service接口
//查询所有用户的购买的所有商品
public List<User> queryUserAndBook() throws Exception;
编写service实现类
@Override
public List<User> queryUserAndBook() throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取mapper对象
UserMapper userMapper =
sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.queryUserAndBook();
//关闭会话
sqlSession.close();
return userList;
}
编写controller
@Test
public void queryUserAndBook()throws Exception{
//获取service对象
UserService userService = new UserServiceImpl();
List<User> userList = userService.queryUserAndBook();
System.out.println(userList);
}
九、动态SQL
9.1 多条件查询
需求:根据传入的数据个数,进行分条件的查询
创建Mapper接口
//根据用户的部分条件进行筛选,将user对象作为形式参数
public List<User> queryUser(User user) throws Exception;
创建Mapper.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="com.woniuxy.mapper.UserMapper">
<!--
需求:根据用户的部分条件进行筛选,将user对象作为形式参数
public List<User> queryUser(User user) throws Exception;
-->
<select id="queryUser" parameterType="User" resultType="User">
select
ti.user_id, user_name, tel, password, age, info_id, real_name,
address, height, weight
from
t_user join t_info ti
on
t_user.user_id = ti.user_id
<where>
<if test="age != null and age != ''">
and age < #{age}
</if>
<if test="info.address != null">
and address = #{info.address}
</if>
</where>
</select>
</mapper>
编写service的接口
//根据不同的条件查询用户数据
public List<User> queryUser(User user) throws Exception;
编写service实现类
public class UserServiceImpl implements UserService {
@Override
public List<User> queryUser(User user) throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取Mapper对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.queryUser(user);
//关闭会话
sqlSession.close();
return userList;
}
}
编写controller
public class UserController {
@Test
public void queryUser() throws Exception{
User user = new User();
//比如:根据地址和年龄进行匹配
user.setAge(18);
Info info = new Info();
info.setAddress("重庆");
user.setInfo(info);
UserService userService = new UserServiceImpl();
List<User> userList = userService.queryUser(user);
System.out.println(userList);
}
}
十、延迟加载
10.1 延迟加载的概念
也叫做懒加载或者按需加载,可以将多表查询用子查询的方式进行拆分,拆分成几个独立的单表查询,需要哪些 数据,只查询需要的即可,如果需要进一步的数据展示,再进行数据库的查询。
10.2 举例说明
mybaits框架默认加载为积极加载,如果要用到延迟加载,需要再核心配置文件中开启延迟加载
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
根据需求,开发一个,满足延迟加载的代码,编写Mapper接口
//查询出所有用户的数据及对应用户的订单数据
public List<User> queryUserAndOrder() throws Exception;
检查pojo类,区分出主表:user表,附表:order表
public class User {
private int userId;
private String userName;
private String password;
private String tel;
private int age;
//声明info对象
private Info info;
//需要通过User类找到Order类,声明成order对象的集合
private List<Order> orderList;
}
mapper.xml配置文件
<!--
需求:查询出所有用户的数据及对应用户的订单数据
public List<User> queryUserAndOrder() throws Exception; 要求:使用延迟加载
-->
<!--定义新的查询语句-->
<select id="queryOrderByUserId" parameterType="int" resultType="Order">
select order_id, user_id, create_time, note from t_order where user_id = #{user_id}
</select>
<!--自定义resultMap-->
<resultMap id="queryUserAndOrderMap" type="User">
<!--主表映射-->
<id column="user_id" property="userId" />
<result column="user_name" property="userName" />
<result column="tel" property="tel" />
<result column="password" property="password" />
<result column="age" property="age" />
<!--附表映射,一对多
延迟加载新增的两个属性:
column:要用于子查询的条件字段
select:新的查询语句,值为新的select的id值
-->
<collection property="orderList" ofType="Order" column="user_id" select="queryOrderByUserId">
<!--如果是延迟加载,这里空着-->
</collection>
</resultMap>
<select id="queryUserAndOrder" resultMap="queryUserAndOrderMap">
select user_id, user_name, tel, password, age from t_user
</select>
service接口
//查询所有用户及用户的订单数据
public List<User> queryUserAndOrder() throws Exception;
service的实现类
@Override
public List<User> queryUserAndOrder() throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取Mapper对象
UserMapper userMapper =
sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.queryUserAndOrder();
sqlSession.close();
return userList;
}
controller类
@Test
public void queryUserAndOrder()throws Exception{
UserService userService = new UserServiceImpl();
List<User> userList = userService.queryUserAndOrder();
//遍历userList
for(int i = 0;i < userList.size();i++){
User user = userList.get(0);
System.out.println("编号:"+user.getUserId()+",姓名"+user.getUserName());
//获取订单对象
List<Order> orderList = user.getOrderList();
System.out.println(orderList);
}
}
十一、Mybatis的缓存
在mybatis的框架中,存在一级缓存和二级缓存,mybatis的缓存指的是第一次查询连接数据库实现查询功能,第二次如果是相同的查询语句,则取缓存里面拿数据,不再连接数据库,以此降低数据库的网络IO
11.1一级缓存
在Mybatis框架中,一级缓存默认开始,不再需要程序员做处理,一级缓存的作用范围在一个会话期间 (SqlSession)
现在用一个代码演示缓存,查看日志中是否执行了sql语句来判断是否查询了数据库。
实例代码:
效果1:当两条sql语句在同一个sqlSession之间,并且查询的条件一致,查看结果:日志中查询了数据库多少次
效果2:在一个会话期间,两条查询语句中间新增一条修改的sql语句并提交事务
效果3:(预留)当修改的语句和缓存区里面的数据无关的时候,是否会清空该缓存
@Override
public void testCache(User user) throws Exception {
//获取工厂
SqlSessionFactory factory = FactoryUtil.newInstance();
//开启会话
SqlSession sqlSession = factory.openSession();
//获取Mapper对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User u1 = userMapper.queryUserById(user.getUserId());
System.out.println("第一次查询:"+u1);
User u2 = userMapper.queryUserById(user.getUserId());
System.out.println("第二次查询:"+u2);
//关闭会话
sqlSession.close();
}
11.2二级缓存
二级缓存相对于一级缓存范围更大,范围区间在整个mapper区间,二级缓存数据存储位置在硬盘,如果要使用二级缓存,需要被操作的pojo类序列化,mybatis框架不会默认开启二级缓存,需要在核心配置文件中开启二级缓存,并且在对应的mapper配置文件中标识该mapper开启二级缓存。
在核心配置文件中开启二级缓存
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/> </settings>
在mapper中开启二级缓存
<mapper namespace="com.woniuxy.mapper.UserMapper">
<!--标识该mapper开启了二级缓存-->
<cache />
</mapper>
对对应的pojo类进行序列化
public class User implements Serializable { ......}
测试
结果1:如果两个一样的查询语句,当开启了二级缓存之后,那么就算不在同一个会话期间,第二次查询还是走的缓存空间
结果2:在两个查询语句之间,做一次更新操作,并提交事务。现象:如果中间有修改操作,则清空缓存,第二次查询走数据库十二、使用注解开发MyBatis应用
MyBatis支持注解开发,可使用注解替代映射器XML文件,但对于同一个映射器接口不能既用注解又用映射器XML文件,否则会导致MyBatis无法确定到底使用哪种方式而报错。
12.1、CURD操作
MyBatis实现CURD操作有对应的注解:@Select,@Update,@Insert,@Delete。
需求:使用注解对t_student进行CURD操作。
创建映射器接口:src/com/wsjy/mapper/StudentMapper2.java ```java public interface StudentMapper2 { /**- 查询所有学生信息
@return / @Select(“select from t_student”) List
findAll(); /**
- 根据学号查询学生信息
@return / @Select(“select from t_student where sid=#{sid}”) Student findStudentBySid(int sid);
/**
- 新增学生
@param student */ @Insert(“insert into t_student values(#{sid},#{sname},#{ssex},#{sage},#{saddress},#{sbirthday},#{cid})”) void addStudent(Student student);
/**
- 根据学号删除学生信息
@param sid */ @Delete(“delete from t_student where sid=#{sid}”) void deleteStudentBySid(int sid);
/**
- 根据学号修改学生信息
- @param student */ @Update(“update t_student set sname=#{sname},ssex=#{ssex} where sid=#{sid}”) void updateStudent(Student student);
}
在测试类中运行以上方法都可正常执行,从运行结果来看,在查询学生信息时,对应的班级和科目信息为null,代表并没有级联查询班级和科目,可使用注解实现级联查询。
<a name="VLPBA"></a>
#### 12.2、级联
MyBatis通过@Results,@Result,@One,@Many来实现级联操作。
- @Results:<resultMap>
- @Result:<result>,通过id属性来区分<id>(true)和<result>(false或省略不写)
- @One:<association>
- @Many:<collection>
需求:在查询学生信息的同时,级联查询班级和科目信息<br />修改映射器接口:src/com/wsjy/mapper/StudentMapper2.java
```java
public interface StudentMapper2 {
......
/**
* 根据学生编号查询学生信息,级联查询该学生选修的所有科目信息、所属班级信息
* @param sid
* @return
*/
@Select({"select * from t_student where sid=#{sid}"})
@Results(id = "studentMap",value = {
@Result(id = true,property = "sid",column = "sid"),
@Result(property = "sname",column = "sname"),
@Result(property = "ssex",column = "ssex"),
@Result(property = "sage",column = "sage"),
@Result(property = "saddress",column = "saddress"),
@Result(property = "sbirthday",column = "sbirthday"),
@Result(property = "cid",column = "cid"),
@Result(property = "classes",column = "cid",
one = @One(select = "com.wsjy.mapper.ClassesMapper.findClassByCid",
fetchType = FetchType.EAGER)),
@Result(property = "subs",column = "sid",
many = @Many(select = "com.wsjy.mapper.SubjectMapper.findSubjectsBySid",
fetchType = FetchType.LAZY))
})
Student findStudentBySid(int sid);
}
鉴别器也可以使用注解:@TypeDiscriminator,@Case来完成。
从以上案例可以看出,使用注解实质上并没有减少任何配置,只是将映射器XML文件中的配置全部转移到java源文件中来而已,同时造成代码结构混乱,可读性差,SQL语句与java代码严重耦合,程序功能一旦有所变化,必须修改java源码等一系列问题。
12.3、缓存
缓存主要是指二级缓存,因为一级缓存自动开启,可以直接使用。使用注解开启二级缓存,非常简单,只需要在映射器接口上使用@CacheNamespace(blocking = true)即可开启。
@CacheNamespace(blocking = true)
public interface StudentMapper2 {
......
}
12.4、动态SQL
mybatis3中增加了使用注解来配置Mapper的新特性,常用的注解有:
@SelectProvider
@UpdateProvider
@InsertProvider
@DeleteProvider。
以上注解用于Mapper接口的方法上,使用type属性来指定由哪个类来提供SQL语句,使用method属性指定由type属性指定的类中哪个具体的方法提供SQL。
示例:
public interface UserMapper {
//指定被@SelectProvider注解的方法使用SqlProvider类的selectUser方法提供的SQL语句
@SelectProvider(type = SqlProvider.class, method = "selectUser")
@ResultMap("userMap")
public User getUser(long userId);
}
@SelectProvider注解用于生成查询用的sql语句,有别于@Select注解,@SelectProvide指定一个Class及其方法,并且通过调用Class上的这个方法来获得sql语句。在上例中,获取查询sql的方法是SqlProvider.selectUser。
@ResultMap注解用于从查询结果集RecordSet中取数据然后拼装实体bean。
编写SqlProvider方式一
public class SqlProvider {
public String selectUser(long userId) {
return "select * from user where userId=" + userId;
}
}
编写SqlProvider方式二
public class SqlProvider{
/**
* list类型:mapper方法形参和sql构建器方法形参必须同时加@Param注解
* @param ids
* @return
*/
public String deleteByIdsSql(@Param("ids") List<Integer> ids){
return new SQL(){{
DELETE_FROM("t_test");
String idStr="";
for (int i = 0; i < ids.size(); i++) {
if (i<ids.size()-1)
idStr+=ids.get(i)+",";
else
idStr+=ids.get(i);
}
WHERE("id in ("+idStr+")");
}}.toString();
}
/**
* 单参数:sql构建器方法参数上同时加上@Param注解,
* 单参数时,mapper方法参数上可不加@Param注解,但建议加上
* @return
*/
public String selectByIdsSql(@Param("id") Integer id){
return new SQL()
.SELECT("*")
.FROM("t_goods")
.WHERE("id="+id)
.toString();
}
}
使用第二种方式的优势是将SQL语句进行结构化,层次分明,避免了第一种形式书写复杂SQL时不便于阅读的问题,具体规则可参见mybatis官网: https://mybatis.org/mybatis-3/zh/statement-builders.html
注解实现动态SQL拼接
1、编写Mapper层代码
//type:产生SQL语句的类型 method:类中真正产生SQL语句的方法名
@DeleteProvider(type = BuildSQL.class,method = "getSQL")
int deleteByCartId(List<Integer> ids);
2、编写Mapper层注解中的方法
public class BuildSQL{
/**
* 产生SQL语句的方法
* 根据传递的参数,动态构建SQL语句
* @return
*/
public String getSQL(List<Integer> ids){
SQL sql = new SQL().DELETE_FROM("t_cart");
String condition="id in(";
//遍历id集合,生成sql条件
for (int i = 0; i < ids.size(); i++) {
condition+=ids.get(i)+",";
}
condition = condition.substring(0, condition.length() - 1)+")";
sql.WHERE(condition);
return sql.toString();
}
}
其他增、改、查操作类似,请查阅mybatis官网中的java api条目下的注解部分。
注意:
如果只想实现一次删除多条数据库表记录,可以不使用mybatis的方式处理,而在业务层中进行处理:将要删除的id集合遍历,在循环中逐条删除。