MyBatis学习(四)

Web中应用MyBatis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.xiaoguan.bank.dao;

import com.xiaoguan.bank.pojo.Account;

/**
* 账户DAO对象,负责t_act的CRUD
*/
public interface AccountDao {
/**
* 根据账户查询账户信息
* @param act 账号
* @return 账户信息
*/
Account selectByActno(String act);

/**
* 更新账户信息
* @param act 账号类
* @return 更新条数
*/
int updateByActno(Account act);

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.xiaoguan.bank.exceptions;

/**
* 余额不足异常
*/
public class MoneyNotEnough extends Exception{
public MoneyNotEnough() {
}

public MoneyNotEnough(String message) {
super(message);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.xiaoguan.bank.exceptions;

/**
* 转账异常
*/
public class TransferException extends Exception{
public TransferException() {

}

public TransferException(String message) {
super(message);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.xiaoguan.bank.pojo;

import java.util.Objects;

/**
* 账户类,封装账户表
*/
public class Account {
private Long id;
private String actno;
private Double balance;

public Account() {

}

public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getActno() {
return actno;
}

public void setActno(String actno) {
this.actno = actno;
}

public Double getBalance() {
return balance;
}

public void setBalance(Double balance) {
this.balance = balance;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return Objects.equals(getId(), account.getId()) && Objects.equals(getActno(), account.getActno()) && Objects.equals(getBalance(), account.getBalance());
}

@Override
public int hashCode() {
return Objects.hash(getId(), getActno(), getBalance());
}

@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.xiaoguan.bank.service;

import com.xiaoguan.bank.exceptions.MoneyNotEnough;
import com.xiaoguan.bank.exceptions.TransferException;

/**
* 账户业务类
*/
public interface AccountService {
/**
* 账户转账业务
* @param fromAct 转出账户
* @param toAct 转入账户
* @param money 转账金额
*/
void transfer(String fromAct ,String toAct ,double money) throws MoneyNotEnough, TransferException;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.xiaoguan.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;

/**
* 工具类,可以动态生成Dao的实现类
*/
public class GenerateDaoproxy {
public static Object generate(SqlSession sqlSession,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 -> {
CtMethod ctMethod = null;
try {
StringBuilder methodCode=new StringBuilder();
//Account selectByActno(String act);
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++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());
methodCode.append(" ");
methodCode.append("arg"+i);
if (i!=parameterTypes.length-1){
methodCode.append(",");
}
}
methodCode.append(")");
methodCode.append("{");
//需要方法体中的代码
methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.xiaoguan.bank.utils.SqlSessionUtils.openSession();");
//需要知道是什么类型的sql语句
String sqlId=daoInterface.getName()+"."+method.getName();
SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
if (sqlCommandType== SqlCommandType.INSERT) {

}
if (sqlCommandType== SqlCommandType.DELETE) {

}
if (sqlCommandType== SqlCommandType.UPDATE) {
methodCode.append("return sqlSession.update(\""+sqlId+"\", arg0);");
}
if (sqlCommandType== SqlCommandType.SELECT) {
methodCode.append("return ("+method.getReturnType().getName()+")sqlSession.selectOne(\""+sqlId+"\", arg0);");

}
methodCode.append("}");
ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
Object obj=null;
try {
Class<?> aClass = ctClass.toClass();
obj = aClass.newInstance();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return obj;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.xiaoguan.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;

/**
* MyBatis工具类
*/
public class SqlSessionUtils {
private static SqlSessionFactory sqlSessionFactory;
private SqlSessionUtils() {}
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
private static ThreadLocal<SqlSession> local=new ThreadLocal<>();
/**
* 获取会话对象
* @return 会话对象
*/
public static SqlSession openSession(){
SqlSession sqlSession = local.get();
if (sqlSession==null) {
sqlSession=sqlSessionFactory.openSession();
local.set(sqlSession);
}
return sqlSession;

}

/**
* 关闭sqlSession对象
* @param sqlSession
*/
public static void close(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.close();
//移除sqlSession对象和当前线程绑定关系
local.remove();
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.xiaoguan.bank.web;

import com.xiaoguan.bank.exceptions.MoneyNotEnough;
import com.xiaoguan.bank.exceptions.TransferException;
import com.xiaoguan.bank.service.AccountService;
import com.xiaoguan.bank.service.impl.AccountServiceImpl;
import com.xiaoguan.bank.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

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;

@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
private AccountService accountService = new AccountServiceImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
Double money =Double.parseDouble(request.getParameter("money"));
//调用service方法完成转账,(调业务层)
try {
accountService.transfer(fromActno,toActno,money);
//到这里表示转账成功了
response.setContentType("text/html;charset=UTF-8");
response.sendRedirect(request.getContextPath()+"/success.html");
} catch (MoneyNotEnough e) {
SqlSession sqlSession = SqlSessionUtils.openSession();
SqlSessionUtils.close(sqlSession);
response.sendRedirect(request.getContextPath()+"/error1.html");
} catch (TransferException e) {
SqlSession sqlSession = SqlSessionUtils.openSession();
sqlSession.rollback();
SqlSessionUtils.close(sqlSession);
response.sendRedirect(request.getContextPath()+"/error2.html");
} catch (Exception e){
SqlSession sqlSession = SqlSessionUtils.openSession();
sqlSession.rollback();
SqlSessionUtils.close(sqlSession);
response.sendRedirect(request.getContextPath()+"/error2.html");
}
//调用View完成展示结果
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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.xiaoguan.bank.dao.AccountDao">
<!--要想使用代理机制namespace必须为接口全名,id为接口里面的方法名-->
<select id="selectByActno" resultType="com.xiaoguan.bank.pojo.Account">
select * from t_act where actno=#{actno};
</select>
<update id="updateByActno">
update t_act set balance=#{balance} where actno=#{actno};
</update>
</mapper>
1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=68963120g
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<!--mybatis log configure-->
<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"/>

<!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>

</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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 resource="jdbc.properties"/>
<environments default="mybatis">
<environment id="mybatis">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="AccountMapper.xml"/>
</mappers>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0"
metadata-complete="false">
<!-- <servlet>-->
<!-- <servlet-name>test</servlet-name>-->
<!-- <servlet-class>com.xiaoguan.bank.web.AccountServlet</servlet-class>-->
<!-- </servlet>-->
<!-- <servlet-mapping>-->
<!-- <servlet-name>test</servlet-name>-->
<!-- <url-pattern>/transfer</url-pattern>-->
<!-- </servlet-mapping>-->
</web-app>

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账报告</title>
</head>
<body>
<h1 >余额不足!!!</h1>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账报告</title>
</head>
<body>
<h1 >转账失败,未知原因!!!</h1>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!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>
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账报告</title>
</head>
<body>
<h1 >转账成功!!!</h1>
</body>
</html>

Javassist生成类

1
2
3
4
5
6
7
8
9
10
package com.xiaoguan.javassist.bank.dao;

public interface AccountDao {
void delete();
int insert(String actno);
int update(String actno,Double balance);
String selectByActno(String actno);

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.xiaoguan.javassist;

import com.xiaoguan.javassist.bank.dao.AccountDao;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.junit.Test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class JavassistTest {
@Test
public void testGenerateAccountDaoImpl() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("com.xiaoguan.bank.dao.impl.AccountDaoImpl");
CtClass ctInterface = pool.makeInterface("com.xiaoguan.javassist.bank.dao.AccountDao");
ctClass.addInterface(ctInterface);
Method[] declaredMethod = AccountDao.class.getDeclaredMethods();
Arrays.stream(declaredMethod).forEach(method -> {
try {
StringBuilder methodCode=new StringBuilder();
methodCode.append("public ");
methodCode.append(method.getReturnType().toString().replace("class",""));//
methodCode.append(" ");
methodCode.append(method.getName());//追加方法名
methodCode.append("(");
//拼接参数
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());
methodCode.append(" ");
methodCode.append("arg"+i);
if(i!=parameterTypes.length-1){
methodCode.append(",");
}
}
methodCode.append("){System.out.println(8888);");
String simpleName = method.getReturnType().getSimpleName();
if ("void".equals(simpleName)) {

} else if ("int".equals(simpleName)) {
methodCode.append("return 1;");
} else if ("String".equals(simpleName)) {
methodCode.append("return \"Hello\";");
}
methodCode.append("}");
System.out.println(methodCode);
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
Class<?> aClass = ctClass.toClass();
AccountDao accountDao = (AccountDao) aClass.newInstance();
accountDao.insert("aaaa");
accountDao.delete();
accountDao.update("111",12.0);
accountDao.selectByActno("111");

}
@Test
public void testGenerateImpl() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("com.xiaoguan.bank.dao.impl.AccountDaoImpl");
CtClass ctInterface = pool.makeInterface("com.xiaoguan.javassist.bank.dao.AccountDao");
ctClass.addInterface(ctInterface);
CtMethod ctMethod = CtMethod.make("public void delete(){System.out.print(\"hello delete!\");}", ctClass);

ctClass.addMethod(ctMethod);
Class<?> aClass = ctClass.toClass();
AccountDao account = (AccountDao) aClass.newInstance();
account.delete();
}
@Test
public void testGenerateFirstClass() throws CannotCompileException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//获取类池,这个类池就是用来生成class的
ClassPool pool = ClassPool.getDefault();
//制造类
CtClass ctClass = pool.makeClass("com.xiaoguan.bank.dao.impl.AccountDaoImpl");
String methodCode="public void insert(){System.out.print(123456789);}";
CtMethod ctMethod = CtMethod.make(methodCode, ctClass);
ctClass.addMethod(ctMethod);
ctClass.toClass();
//类加载到JVM中
Class<?> aClass = Class.forName("com.xiaoguan.bank.dao.impl.AccountDaoImpl");
Object obj = aClass.newInstance();
Method insertMethod = aClass.getDeclaredMethod("insert");
insertMethod.invoke(obj);

}
}