MyBatis是增强版的jdbc,把一些功能通过反射封装好了
<packaging>jarpackaging>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
dependencies>
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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="xxxx"/>
<property name="password" value="xxxx"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="CarMapper.xml"/>
mappers>
configuration>
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<insert id="">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,1003,"面包车",13.00,"2020-10-13","飞行汽车")
insert>
mapper>
在xxxMapper中编写sql代码(在3中已完成)
把xxxMapper.txt文件路径放入mybatis-config.txt中(在2中已完成)
<mappers>
<mapper resource="CarMapper.xml"/>
mappers>
在mybatis-config.xml中有一行为
<transactionManager type="JDBC"/>
type类型可以写成两种,一种是JDBC另一种是MANAGED(不区分大小写)
注意事项:
SqlSession sqlSession = null;
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
sqlSession = sqlSessionFactory.openSession();
int count = sqlSession.insert("insertCar");
System.out.println("新增了记录"+count);
//提交事务
sqlSession.commit();
} catch (Exception e) {
if (sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
}finally {
if (sqlSession != null) {
sqlSession.close();
}
}
在maven中添加junit依赖
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
新建test包,以及创建CarMapperTest类
在新建的类中编写MyBatis代码
public class CarMapperTest {
@Test
public void insertCar(){
SqlSession sqlSession = null;
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));
sqlSession = sqlSessionFactory.openSession();
int count = sqlSession.insert("insertCar");
System.out.println("新增了记录"+count);
//提交事务
sqlSession.commit();
} catch (Exception e) {
if (sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
}finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
logback、log4j、log4j2是同一个作者编写的
若使用STDOUT_LOGGING,需要在mybatis-config.xml里添加配置文件
注意:settings标签必须添加在configuration下面
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
configuration>
这边不使用STDOUT_LOGGING日志组件,我们使用最常用的logback组件
配置步骤:
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
<scope>testscope>
dependency>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<configuration debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="UTF-8">
<pattern>[%thread] %-5level %logger{50} - %msg%npattern>
encoder>
appender>
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
root>
configuration>
在编写代码中,每次都通过SqlSessionFactoryBuilder三步骤获取session太麻烦,我们编写一个工具类get会比较方便点
public class SqlSessionUtilTest {
/**
* 测试工具类
*/
@Test
public void openSessionTest(){
SqlSession session = SqlSessionUtil.openSession();
int count = session.insert("insertCar");
System.out.println(count);
session.commit();
session.close();
}
}
第一个入门程序已完成,现在我们来做MyBatis的CRUD操作
入门程序有个问题:实战的时候新增数据表行数据不可能是固定值
回顾:
<mapper namespace="org.mybatis.example.BlogMapper">
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,1003,"面包车",13.00,"2020-10-13","飞行汽车")
insert>
mapper>
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,#{},#{},#{},#{},#{})
insert>
public class CarMapperTest {
@Test
public void insertCar(){
SqlSession session = SqlSessionUtil.openSession();
Map<String,Object> map = new HashMap<>();
map.put("carNum","1004");
map.put("brand","比亚迪汉");
map.put("guidePrice",160.00);
map.put("produceTime","2022-06-08");
map.put("carType","新能源汽车");
int count= session.insert("insertCar", map);
System.out.println("新增的条目:"+count);
session.commit();
session.close();
}
}
<mapper namespace="org.mybatis.example.BlogMapper">
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
insert>
mapper>
public class Car {
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
//此处忽略构造方法、getting setting方法
.....
}
@Test
public void insertCar(){
SqlSession session = SqlSessionUtil.openSession();
Car car = new Car(null,"1005","比亚迪秦",30.0,"2020-10-20","新能源");
int count= session.insert("insertCar", car);
System.out.println("新增的条目:"+count);
session.commit();
session.close();
}
<mapper namespace="org.mybatis.example.BlogMapper">
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
insert>
mapper>
<delete id="deleteById">
delete from t_car where id = #{id}
delete>
@Test
public void deleteById(){
SqlSession session = SqlSessionUtil.openSession();
session.delete("deleteById",17);
session.commit();
session.close();
}
<update id="updateById">
update t_car
set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
where id=#{id}
update>
@Test
public void testUpdateById(){
SqlSession session = SqlSessionUtil.openSession();
Car car = new Car(3L,"5123","哈哈车",1.5,"2011-01-04","新技术");
int count = session.update("updateById", car);
System.out.println(count);
session.commit();
session.close();
}
<select id="selectOneById" resultType="com.powernode.mybatis.pojo.Car">
select car_num as carNum,
brand,guide_price as guidePrice,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car
where id = #{id}
select>
@Test
public void selectOneById(){
SqlSession session = SqlSessionUtil.openSession();
Car car = session.selectOne("selectOneById",16L);
System.out.println(car);
session.close();
}
注意事项:
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select car_num as carNum,
brand,guide_price as guidePrice,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car
select>
@Test
public void selectAll(){
SqlSession session = SqlSessionUtil.openSession();
List<Car> cars = session.selectList("selectAll");
cars.forEach( car -> System.out.println(car));
session.close();
}
在mapper.xml文件中有一个namespasce,这个属性是用来指定命名空间的,用来防止id重复
在java程序中的写法:
List<Car> cars = session.selectList("namespace.selectAll");
这样写才是严谨、完整的
一个environment对应一个SqlSessionFactory
一个SqlSessionFactory对应一个数据库
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
<environment id="development2">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode2"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
public class testConfiguration {
@Test
public void testEnvironment() throws IOException {
SqlSessionFactoryBuilder sessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = sessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "development2");
SqlSession session = sessionFactory.openSession();
int count = session.insert("insertCar");
System.out.println(count);
session.commit();
session.close();
}
}
在mybatis-config.xml配置文件中,有transactionManager子标签,表示设置MyBatis的事务管理器
<transactionManager type="JDBC"/>
MyBatis中有两个事务管理器
MyBatis底层有一个Transaction接口,实现两个事务
<dataSource type="POOLED">
.......
dataSource>
问:这个标签有啥用
答:dataSource表示数据源,用来获取Connection对象
它的type属性可以填写三个值:
不同类型的数据源下有不同的属性,比如
<dataSource type="POOLED">
....
<properties/>
....
dataSource>
和
<dataSource type="JNDI">
....
<properties/>
....
dataSource>
的
<properties/>
数量、内容是不一样的,具体需要看官方文档
JDNI是:java命名目录接口,Tomcat服务器实现了这个规范
假如需要用第三方数据源,Tomcat中实现了这个数据源,则在properties中填写对应的数据,要和tomat对应上
问:使用连接池有什么好处?
答:
3. 迟内的Connection数量是固定的,比如池子大小是5,如果5个连接都被占用,第6个要获取连接就先等待,数量固定
4. 假如有人一直F5刷新,没有用连接池的话,就会一直创建Connection对象,如果实例化对象过多,可能会导致服务器宕机,数量固定
5. 有新请求,第一反应去池中查找,可以增加效率
<property name="poolMaximumActiveConnections " value="10"/>
<property name="poolMaximumIdleConnections " value="5"/>
<property name="poolMaximumCheckoutTime " value="20000"/>
<property name="poolTimeToWait " value="2000"/>
在configuration标签下有一个properties子标签,是用来设置变量的
<property name="key" value="value"/>
这样写相当于往map集合里放入一个key,可以通过key拿到value,在mybatis-config.xml的其他地方,可以写成${key}获取到value
<properties resource="jdbc.properties"/>
然后在外部新建一个jdbc.properties,如(等号左边是key右边是value)
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/powernode
username=root
password=root
不推荐这种写法
<properties url="file:///D://jdbc.properties"/>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
<dependency>
<groupId>servletapigroupId>
<artifactId>servletapiartifactId>
<version>2.4-20040521version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
dependencies>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
web-app>
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.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>
<environment id="development2">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode2"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="ActMapper.xml"/>
mappers>
configuration>
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/powernode
username=root
password=root
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="account">
<insert id="insertAct">
insert into t_act(id,name,balance)
values (null,#{name},#{balance})
insert>
mapper>
<configuration debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="UTF-8">
<pattern>{%thread} %-5level %logger{50} - %msg%npattern>
encoder>
appender>
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
root>
configuration>
public class Account {
private Long id;
private String name;
private Double balance;
......
//此处省略构造方法、getting setting toString方法,请自行创建
package com.powpernode.bank.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
/**
* @author huochai
* @date 2022/10/15 9:22
*/
public class SqlSessionUtil {
/**
* 一个sessionFactory对应一个environment(数据库),所以不要每次运行都new一个新的
*/
/**
* 构造方法设置为私有的,防止被实例化
*/
private SqlSessionUtil(){}
private static SqlSessionFactory sessionFactory;
static {
try {
sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession openSession(){
SqlSession session = sessionFactory.openSession();
return session;
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>银行转账title>
head>
<body>
<form action="/bank/transfer" method="post">
转出账户:<input type="text" name="fromActno"><br>
转入账户:<input type="text" name="toActno"><br>
转账金额<input type="text" name="money"><br>
<input type="submit" value="转账">
form>
body>
html>
根据前端请求发送的路线完善后端代码
package com.powpernode.bank.web;
import com.powpernode.bank.exception.MoneyNotEnoughException;
import com.powpernode.bank.exception.TransferNotSuccessException;
import com.powpernode.bank.service.AccountService;
import com.powpernode.bank.service.impl.AccountServiceImpl;
import javax.jws.WebService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author huochai
* @date 2022/10/16 15:15
*/
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
/**
* 调用业务类处理业务
*/
private AccountService accountService = new AccountServiceImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//接收前端传来的参数
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
Double money = Double.parseDouble(request.getParameter("money"));
//调用业务类处理转账
try {
accountService.transfer(fromActno,toActno,money);
response.sendRedirect(request.getContextPath()+"/success.html");
} catch (MoneyNotEnoughException e) {
e.printStackTrace();
response.sendRedirect(request.getContextPath()+"/error1.html");
} catch (TransferNotSuccessException e) {
e.printStackTrace();
response.sendRedirect(request.getContextPath()+"/error2.html");
}
}
}
package com.powpernode.bank.service;
import com.powpernode.bank.exception.MoneyNotEnoughException;
import com.powpernode.bank.exception.TransferNotSuccessException;
/**
* @author huochai
* @date 2022/10/16 15:17
*/
public interface AccountService {
/**
* 转账业务方法
* @param fromAct 转出账户
* @param toAct 转入账户
* @param money 转账金额
* @throws MoneyNotEnoughException 转出账户余额不足异常
* @throws TransferNotSuccessException 转账失败异常
*/
void transfer(String fromAct,String toAct,Double money) throws MoneyNotEnoughException, TransferNotSuccessException;
}
package com.powpernode.bank.service.impl;
import com.powpernode.bank.dao.AccountDao;
import com.powpernode.bank.dao.impl.AccountDaoImpl;
import com.powpernode.bank.exception.MoneyNotEnoughException;
import com.powpernode.bank.exception.TransferNotSuccessException;
import com.powpernode.bank.pojo.Account;
import com.powpernode.bank.service.AccountService;
import javax.jws.WebService;
/**
* @author huochai
* @date 2022/10/16 15:19
* 只负责处理业务逻辑,不涉及数据库修改
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String fromAct, String toAct, Double money) throws MoneyNotEnoughException, TransferNotSuccessException {
//判断余额是否充足
Account fromAccount = accountDao.selectById(Long.parseLong(fromAct));
if (fromAccount.getBalance()<money) {
throw new MoneyNotEnoughException("对不起,余额不足");
}
//将fromAct减少money,toAct增加money
Account toAccount = accountDao.selectById(Long.parseLong(toAct));
fromAccount.setBalance(fromAccount.getBalance()-money);
toAccount.setBalance(toAccount.getBalance()+money);
//更新两个账户
int count = accountDao.updateAccount(fromAccount);
count += accountDao.updateAccount(toAccount);
if (count<2){
throw new TransferNotSuccessException("转账失败,未知错误");
}
}
}
package com.powpernode.bank.dao;
import com.powpernode.bank.pojo.Account;
/**
* @author huochai
* @date 2022/10/16 15:20
*/
public interface AccountDao {
/**
* 根据ID查询账户
* @param id id
* @return 返回账户
*/
Account selectById(Long id);
/**
* 更新用户
* @param account 需要更新的用户
* @return 1表示更新完成
*/
int updateAccount(Account account);
/**
* 插入用户
* @param account 需要插入的用户
* @return 1表示插入完成
*/
int insertAccount(Account account);
}
package com.powpernode.bank.dao.impl;
import com.powpernode.bank.dao.AccountDao;
import com.powpernode.bank.pojo.Account;
import com.powpernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
/**
* @author huochai
* @date 2022/10/16 15:21
*/
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectById(Long id) {
SqlSession session = SqlSessionUtil.openSession();
Account account = session.selectOne("account.selectById", id);
session.close();
return account;
}
@Override
public int updateAccount(Account account) {
SqlSession session = SqlSessionUtil.openSession();
int count = session.update("account.updateById", account);
session.commit();
return count;
}
@Override
public int insertAccount(Account account) {
SqlSession session = SqlSessionUtil.openSession();
int count = session.insert("account.insertAct", account);
session.commit();
return count;
}
}
/**
* @author huochai
* @date 2022/10/16 15:47
*/
public class MoneyNotEnoughException extends Exception{
public MoneyNotEnoughException(){};
public MoneyNotEnoughException(String msg){
super(msg);
}
}
package com.powpernode.bank.exception;
/**
* @author huochai
* @date 2022/10/16 15:51
*/
public class TransferNotSuccessException extends Exception{
public TransferNotSuccessException(){};
public TransferNotSuccessException(String msg){
super(msg);
}
}
目前为止项目里存在一个问题,没有事务处理机制
如果在更新完账户1之后异常,就会出现少钱的现象
/**
* 完善后的SqlSessionUtil工具类
*/
package com.powpernode.bank.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
/**
* @author huochai
* @date 2022/10/15 9:22
*/
public class SqlSessionUtil {
public static ThreadLocal<SqlSession> local = new ThreadLocal();
private SqlSessionUtil(){}
private static SqlSessionFactory sessionFactory;
static {
try {
sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession openSession(){
SqlSession session = local.get();
if (session==null) {
session = sessionFactory.openSession();
local.set(session);
}
return session;
}
public static void close(SqlSession session){
if (session!=null) {
session.close();
local.remove();
}
}
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String fromAct, String toAct, Double money) throws MoneyNotEnoughException, TransferNotSuccessException {
SqlSession session = SqlSessionUtil.openSession();
//判断余额是否充足
Account fromAccount = accountDao.selectById(Long.parseLong(fromAct));
if (fromAccount.getBalance()<money) {
throw new MoneyNotEnoughException("对不起,余额不足");
}
//将fromAct减少money,toAct增加money
Account toAccount = accountDao.selectById(Long.parseLong(toAct));
fromAccount.setBalance(fromAccount.getBalance()-money);
toAccount.setBalance(toAccount.getBalance()+money);
//更新两个账户
int count = accountDao.updateAccount(fromAccount);
count += accountDao.updateAccount(toAccount);
if (count<2){
throw new TransferNotSuccessException("转账失败,未知错误");
}
session.commit();
SqlSessionUtil.close(session);
}
}
名称 | 生命周期 |
---|---|
SqlSessionFactoryBuilder | 只是用来buildFactory的,利用完即可丢弃 |
SqlSessionFactory | 一个数据库对应一个Factory,最好不要丢弃 |
SqlSession | 一个线程对应一个SqlSession |
观察项目还有什么问题,发现DAO层代码很少并且很固定
public class AccountDaoImpl implements AccountDao {
//可以发现第一行都是获取Session,第二行执行,第三行return
@Override
public Account selectById(Long id) {
SqlSession session = SqlSessionUtil.openSession();
Account account = session.selectOne("account.selectById", id);
return account;
}
@Override
public int updateAccount(Account account) {
SqlSession session = SqlSessionUtil.openSession();
int count = session.update("account.updateById", account);
return count;
}
@Override
public int insertAccount(Account account) {
SqlSession session = SqlSessionUtil.openSession();
int count = session.insert("account.insertAct", account);
return count;
}
}
有没有什么框架可以代替这种简单重复的工作呢?
答:可以利用javassist框架
通过使用Javassist对字节码操作为JBoss实现动态“AOP”框架
public class JavassistTest{
@Test
public void generateClass() throws Exception {
//获取类池
ClassPool classPool = ClassPool.getDefault();
// 制造类
CtClass ctClass = classPool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造方法
String method = "public void insert(){System.out.println(166);}";
CtMethod make = CtMethod.make(method, ctClass);
// 把方法加到类中
ctClass.addMethod(make);
//在内存中生成类
ctClass.toClass();
Class<?> aClass = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
Object o = aClass.newInstance();
Method insert = aClass.getDeclaredMethod("insert");
insert.invoke(o);
}
}
@Test
public void generateInterface() throws Exception{
//获取类池
ClassPool pool = ClassPool.getDefault();
//制造类
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
//制造接口
CtClass ctClass1 = pool.makeInterface("com.powernode.javassist.test.AccountDao");
//添加接口
ctClass.addInterface(ctClass1);
CtMethod make = CtMethod.make("public void delete(){System.out.println(\"Hello delete\");}", ctClass);
ctClass.addMethod(make);
//装载类
Class<?> aClass = ctClass.toClass();
AccountDao o = (AccountDao) aClass.newInstance();
o.delete();
}
@Test
public void generateInterfaceAll() throws Exception{
//获取类池
ClassPool pool = ClassPool.getDefault();
//制造类
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
//制造接口
CtClass ctInterface = pool.makeInterface("com.powernode.javassist.test.AccountDao");
//类实现接口
ctClass.addInterface(ctInterface);
Method[] declaredMethods = AccountDao.class.getDeclaredMethods();
//制造方法
//方法内容
Arrays.stream(declaredMethods).forEach(method -> {
try {
StringBuffer methodValue = new StringBuffer();
methodValue.append("public ");
methodValue.append(method.getReturnType().getName()+" ");
methodValue.append(method.getName()+"(");
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
methodValue.append(parameterTypes[i].getName()+" ");
methodValue.append("args"+i);
if (i!= parameterTypes.length-1){
methodValue.append(",");
}
}
methodValue.append(")");
methodValue.append("{System.out.println(\"hello all\");");
if ("void".equals(method.getReturnType().getName())){
}else if ("int".equals(method.getReturnType().getName())){
methodValue.append("return 1;");
}else if("java.lang.String".equals(method.getReturnType().getName())){
methodValue.append("return \"666\";");
}
methodValue.append("}");
CtMethod make = CtMethod.make(String.valueOf(methodValue), ctClass);
ctClass.addMethod(make);
} catch (CannotCompileException e) {
e.printStackTrace();
}
});
Class<?> toClass = ctClass.toClass();
AccountDao accountDao = (AccountDao) toClass.newInstance();
accountDao.delete();
accountDao.insert("666",50.00);
accountDao.selectByActno("555");
}
注意:若想利用GenerateDaoProxy,
则Mapper.xml文件的namespace必须为DAO层接口的全类名,
sqlId必须为DAO层接口中的方法
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.powpernode.bank.dao.AccountDao">
<insert id="insertAccount">
insert into t_act(id,name,balance)
values (null,#{name},#{balance})
insert>
<select id="selectById" resultType="com.powpernode.bank.pojo.Account">
select * from t_act where id=#{id}
select>
<update id="updateAccount">
update t_act set name=#{name},balance=#{balance} where id=#{id}
update>
mapper>
package com.powpernode.bank.utils;
import org.apache.ibatis.javassist.CannotCompileException;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author huochai
* @date 2022/10/16 23:30
*/
public class GenerateDaoProxy {
/**
* 这个工具类是框架的开发者提供的
* 开发者可以给使用者规定传进哪些参数
*
* 传进接口,返回实现所有方法的类
* @param daoInterface 接口
* @return Impl类
*/
public static Object generate(SqlSession session, Class daoInterface) {
//类池
ClassPool pool = ClassPool.getDefault();
//制造类
CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
//制造接口
CtClass ctInterface = pool.makeInterface(daoInterface.getName());
ctClass.addInterface(ctInterface);
Method[] declaredMethods = daoInterface.getDeclaredMethods();
Arrays.stream(declaredMethods).forEach(method -> {
try {
StringBuffer methodCode = new StringBuffer();
//添加修饰符
methodCode.append("public ");
//添加返回值
methodCode.append(method.getReturnType().getName()+" ");
methodCode.append(method.getName());
methodCode.append("(");
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
methodCode.append(parameterTypes[i].getName()+" ");
methodCode.append("arg"+i);
if (i!= parameterTypes.length-1){
methodCode.append(",");
}
}
methodCode.append("){");
/**
* 括号中间需要写对应的session.insert或session.select方法
*/
String sqlId = daoInterface.getName()+"."+method.getName();
SqlCommandType sqlCommandType = session.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
methodCode.append("org.apache.ibatis.session.SqlSession session = com.powpernode.bank.utils.SqlSessionUtil.openSession();");
if(sqlCommandType == SqlCommandType.INSERT){
}
if(sqlCommandType == SqlCommandType.DELETE){
}
if(sqlCommandType == SqlCommandType.UPDATE){
methodCode.append("return session.update(\""+sqlId+"\", arg0);");
}
if(sqlCommandType == SqlCommandType.SELECT){
String resultType = method.getReturnType().getName();
methodCode.append("return ("+resultType+")session.selectOne(\""+sqlId+"\", arg0);");
}
methodCode.append("}");
System.out.println(methodCode.toString());
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (CannotCompileException e) {
e.printStackTrace();
}
});
Object obj = null;
try {
Class<?> toClass = ctClass.toClass();
obj = toClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
最后在业务层引用Dao的时候改一下即可:
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(),AccountDao.class);
.....
写完以后得知一个好消息,MyBatis已经实现了映射机制,不用自己手写代码了(要求和上面一样,对namespace以及sqlId有格式要求)
//自己写的
private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(),AccountDao.class);
//MyBatis自带
private AccountDao accountDao = SqlSessionUtil.openSession().getMapper(AccountDao.class);
新建maven模块mybatis-005-crud2
添加maven依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MyBatisartifactId>
<groupId>org.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<packaging>jarpackaging>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
dependencies>
<groupId>com.powernodegroupId>
<artifactId>mybatis-005-crud2artifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
project>
添加mapper类
/**
* @author huochai
* @date 2022/10/17 9:38
* 在MyBatis中,一般不叫XXXDao,一般叫xxxMapper
*/
public interface CarMapper {
/**
* 插入数据
* @param car
* @return 返回1表示正常
*/
int insert(Car car);
/**
* 根据ID删除
* @param id
* @return 返回1表示正常
*/
int deleteById(Long id);
/**
* 更新数据
* @param car
* @return 返回1表示正常
*/
int update(Car car);
/**
* 根据ID查询
* @param id
* @return 返回1表示正常
*/
Car selectById(Long id);
/**
* 查询所有的数据
* @return 返回给List集合
*/
List<Car> selectAll();
}
编写CarMapper.xml配置文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<insert id="insert">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null, #{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
insert>
<delete id="delete">
delete from t_car where id=#{id}
delete>
<update id="update">
update t_car
set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
where id=#{id}
update>
<select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from t_car where id=#{id}
select>
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from t_car
select>
mapper>
新建工具类,SqlSessionUtil
public class SqlSessionUtil {
public static ThreadLocal<SqlSession> local = new ThreadLocal();
private SqlSessionUtil(){}
private static SqlSessionFactory sessionFactory;
static {
try {
sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession openSession(){
SqlSession session = local.get();
if (session==null) {
session = sessionFactory.openSession();
local.set(session);
}
return session;
}
public static void close(SqlSession session){
if (session!=null) {
session.close();
local.remove();
}
}
}
编写测试用例
public class TestMapper {
@Test
public void insert(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
Car car = new Car(null,"5556","兰博基尼六",1952.1,"2060-02-06","跑车");
mapper.insert(car);
session.commit();
}
@Test
public void delete(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
mapper.deleteById(21L);
session.commit();
}
@Test
public void update(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
Car car = new Car(18L,"5556","兰博基尼六",1952.1,"2060-02-06","跑车");
mapper.update(car);
session.commit();
}
@Test
public void selectById(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
Car car = mapper.selectById(18L);
System.out.println(car);
}
@Test
public void selectAll(){
SqlSession session = SqlSessionUtil.openSession();
CarMapper mapper = session.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
cars.forEach(car -> System.out.println(car));
}
}
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<select id="selectByType" resultType="com.powernode.mybatis.pojo.Car">
select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from t_car
order by produce_time ${ascOrDesc}
select>
mapper>
public class CarMapperTest {
@Test
public void selectAllSortedTest(){
SqlSession session = SqlSessionUtil.openSession();
List<Car> cars = session.getMapper(CarMapper.class).selectByType("desc");
cars.forEach( car -> System.out.println(car));
session.close();
}
}
delete from t_car where id in (123,456,789)
//用美元括号
delete from t_car where id in (${})
<select id="selectLikeSome" resultType="com.powernode.mybatis.pojo.Car">
select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from t_car
where car_type like Concat('%',#{str},'%')
</select>
<select id="selectLikeSome" resultType="com.powernode.mybatis.pojo.Car">
select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
from t_car
where car_type like "%"#{str}"%"
</select>
namespace不能起别名
所有别名不区分大小写
<typeAliases>
<typeAlias type="com.powernode.mybatis.pojo.Car" alias="car"/>
<typeAlias type="com.powernode.mybatis.pojo.Car"/>
<package name="com.powernode.mybatis.pojo"/>
typeAliases>
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper url="file://c:/CarMapper.xml"/>
<mapper class="com.powernode.mybatis.mapper.CarMapper"/>
<package name="com.powernode.mybatis.mapper"/>
mappers>
插入一条数据的时候,自动返回主键到制定属性中
如
useGeneratedKeys=“true” 表示使用自动生成的主键值
keyProperty=“id” 制定属性值赋值给对象的哪个属性
<insert id="insertCar" useGeneratedKeys="true" keyProperty="id">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
insert>
@Test
public void insertCar(){
SqlSession session = SqlSessionUtil.openSession();
Car car = new Car(null,"9851","比亚迪ao",30.0,"2020-10-20","新能源");
session.getMapper(CarMapper.class).insertCar(car);
System.out.println(car);
session.close();
}
结果:Car{id=22, carNum=‘9851’, brand=‘比亚迪ao’, guidePrice=30.0, produceTime=‘2020-10-20’, carType=‘新能源’}
把自动递增的主键返回给了id