mybatis定义分析和完善基于注解
CURD
持久层接口
package com.Dao;
import com.zheng.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @Author: zheng
* @Data :2019/11/2 14:15
*/
public interface JDBCDao {
List<User> fillAll();
void saveUser(User user);
void updataUser(User user);
void Delete(Integer i);
List<User> selectById(Integer id);
//模糊查询
List<User> findByName(String username);
/**
* 查询总记录
*
*/
int findCount();
}
用户数据模型
package com.zheng;
import java.io.Serializable;
/**
* @Author: zheng
* @Data :2019/11/2 14:13
*/
public class User implements Serializable {
private Integer id;
private String name;
private String age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
主配置文件
<?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="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/db1"/>
<property name="username" value="root"/>
<property name="password" value="zheng"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/zheng/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
具体的配置可以看笔者的另一篇文章https://www.jianshu.com/p/030aa1277e3f
映射配置文件
<?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 用来配置命名空间,主要进行session级别的缓存配置
通常情况,命名空间的值,就是当前操作实体类对象的全名称
-->
<mapper namespace="com.Dao.JDBCDao">
<select id="fillAll" resultType="com.zheng.User">
select * from test1;
</select>
<insert id="saveUser" parameterType="com.zheng.User">
<!--配置插入语句返回 的id-->
<selectKey keyProperty="id" keyColumn="id" resultType="java.lang.Integer" order="AFTER">
select last_insert_id()
</selectKey>
insert into test1(id , name , age) values (#{id} , #{name}, #{age})
</insert>
<update id="updataUser" parameterType="com.zheng.User">
update test1 set name =#{name},age = #{age} where id =#{id}
</update>
<delete id="Delete" parameterType="java.lang.Integer">
delete from test1 where id =#{i}
</delete>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.zheng.User">
select * from test1 where id =#{id}
</select>
<select id="findByName" parameterType="java.lang.String" resultType="com.zheng.User">
<!-- select * from test1 where name like #{name}-->
select * from test1 where name like '%${value}%'
</select>
<select id="findCount" resultType="java.lang.Integer">
select count(id) from test1
</select>
</mapper>
测试类
package com.zheng;
import com.Dao.JDBCDao;
import com.example.SqlSssionFactory;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author: zheng
* @Data :2019/11/2 14:36
*/
public class test {
SqlSession sqlSession;
InputStream in;
JDBCDao mapper;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = builder.build(in);
sqlSession = build.openSession();
mapper = sqlSession.getMapper(JDBCDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.close();
in.close();
}
@Test
public void save() {
User user =new User();
user.setId(5);
user.setName("hao");
user.setAge("18");
System.out.println(user);
mapper.saveUser(user);
sqlSession.commit();
System.out.println(user);
}
@Test
public void select()
{
List<User> users = mapper.fillAll();
for (User user : users)
{
System.out.println(user);
}
}
@Test
public void updata()
{
User user = new User();
user.setId(25);
user.setName("zl");
user.setAge("7");
mapper.updataUser(user);
select();
}
@Test
public void delete()
{
mapper.Delete(25);
select();
}
@Test
public void selectById()
{
List<User> users=mapper.selectById(22);
for (User u : users) {
System.out.println(u);
}
}
@Test
public void SelectByName()
{
// List<User> byName = mapper.findByName("%l%");
List<User> byName = mapper.findByName("l");//区别使用value是直接拼接¥而是用&是?动态语句
for (User u:
byName) {
System.out.println(u);
}
}
@Test
public void selectCount()
{
System.out.println(mapper.findCount());
}
}
Mybtis的参数已经结果集的深入
OGNL表达式
object Graphic Navigation Language 对象图导航图
在写法上把get方法除开了
ParameterType
可以传入简单类型,pojo对象类型,pojo包装对象
可以将一个对象作为参数传入进去然后通过user.userName 的形式调用方法
结果类型的封装
resultType可以规定结果类型
基本类型,pojo的对象或者pojo的列表
如果实体类属性名和数据库列名不一致查询时,数据封装不进去的
解决方法1
取别名:比如
<select id="fillAll" resultType="com.zheng.User">
select id as userId ,name as UserName , age sa UserAge from test1;
</select>
解决方法2建立配置
<resultMap id="userMap" type="com.zheng.User">
<!--主键对应类型-->
<id property="uerId" column="id"></id>
<!--属性对应类型-->
<result property="userName" column="name"></result>
<result property="userAge" column="age"></result>
</resultMap>
<!--当时使用resultMap类型的配置是要导入resultMap属性中-->
<select id="fillAll" resultMap="">
select * from test1;
</select>
了解传统的DAO的实现
mybatis支持我们自己来实现类
代码如下
首先还是需要配置文件对其进行映射的
<?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="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1"/>
<property name="username" value="root"/>
<property name="password" value="zheng"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/zheng/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
映射文件
<?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 用来配置命名空间,主要进行session级别的缓存配置
通常情况,命名空间的值,就是当前操作实体类对象的全名称
-->
< mapper namespace="com.Dao.JDBCDao">
<!--当时使用resultMap类型的配置是要导入resultMap属性中-->
<select id="fillAll" resultType="com.zheng.User">
select * from test1;
</select>
<insert id="saveUser" parameterType="com.zheng.User">
<!--配置插入语句返回 的id-->
<selectKey keyProperty="id" keyColumn="id" resultType="java.lang.Integer" order="AFTER">
select last_insert_id()
</selectKey>
insert into test1(id , name , age) values (#{id} , #{name}, #{age})
</insert>
<update id="updataUser" parameterType="com.zheng.User">
update test1 set name =#{name},age = #{age} where id =#{id}
</update>
<delete id="Delete" parameterType="java.lang.Integer">
delete from test1 where id =#{i}
</delete>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.zheng.User">
select * from test1 where id =#{id}
</select>
<select id="findByName" parameterType="java.lang.String" resultType="com.zheng.User">
<!-- select * from test1 where name like #{name}-->
select * from test1 where name like '%${value}%'
</select>
<select id="findCount" resultType="java.lang.Integer">
select count(id) from test1
</select>
</mapper>
实现持久层接口
package com.Dao;
import com.zheng.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
/**
* @Author: zheng
* @Data :2019/11/3 14:36
*/
public class JDBCDaoImp implements JDBCDao {
private SqlSessionFactory factory;
public JDBCDaoImp(SqlSessionFactory factory)
{
this.factory = factory;
}
public List<User> fillAll() {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
List<User> users=session.selectList("com.Dao.JDBCDao.fillAll");//参数是能获取配置文件的key就是持久层接口+方法名称
session.close();
return users;
}
public void saveUser(User user) {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
session.insert("com.Dao.JDBCDao.saveUser",user);//参数是能获取配置文件的key就是持久层接口+方法名称
session.commit();
}
public void updataUser(User user) {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
session.insert("com.Dao.JDBCDao.updataUser",user);//参数是能获取配置文件的key就是持久层接口+方法名称
session.commit();
}
public void Delete(Integer i) {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
session.delete("com.Dao.JDBCDao.Delete",i);//参数是能获取配置文件的key就是持久层接口+方法名称
session.commit();
}
public User selectById(Integer id) {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
User user=session.selectOne("com.Dao.JDBCDao.selectById", id);//参数是能获取配置文件的key就是持久层接口+方法名称
session.close();
return user;
}
public List<User> findByName(String username) {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
List<User> user=session.selectList("com.Dao.JDBCDao.findByName", username);//参数是能获取配置文件的key就是持久层接口+方法名称
session.close();
return user;
}
public Integer findCount() {
//根据SqlSssionFactory获取SqlSession对象
SqlSession session = factory.openSession();
//调用SqlSession中的方法实现方法
Integer user=session.selectOne("com.Dao.JDBCDao.findCount");//参数是能获取配置文件的key就是持久层接口+方法名称
session.close();
return user;
}
}
测试类验证是否实现了
package com.zheng;
import java.io.Serializable;
/**
* @Author: zheng
* @Data :2019/11/2 14:13
*/
public class User implements Serializable {
private Integer id;
private String name;
private String age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
分析mybatis执行过程
selectList最终是执行的下列方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
最后执行处理结果集
public List<Object> handleResultSets(Statement stmt) throws
SQLException {
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
List<Object> multipleResults = new ArrayList();
int resultSetCount = 0;
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
this.validateResultMapsCount(rsw, resultMapCount);
while(rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
preparedStatement方法执行
excute :能执行CRUD,返回值是一个boolean标识是否有结果集
excuteQuery:只能执行select操作,返回值结果集对象
excuteUpdate:CUD操作,返回值是影响数据库的行数
insert和updata,delete都用这个方法
public int update(String statement, Object parameter) {
int var4;
try {
this.dirty = true;
MappedStatement ms = this.configuration.getMappedStatement(statement);
var4 = this.executor.update(ms, this.wrapCollection(parameter));
} catch (Exception var8) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8);
} finally {
ErrorContext.instance().reset();
}
return var4;
}
然后都走到PreparedStatementHandler
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = this.boundSql.getParameterObject();
KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
keyGenerator.processAfter(this.executor, this.mappedStatement, ps, parameterObject);
return rows;
}
使用代理的分析过程
1.在class DefaultSqlSession
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
2.在class Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
3.class MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
-
class MapperProxyFactory<T>
public T newInstance(SqlSession sqlSession) { MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); return this.newInstance(mapperProxy);
}
5.class MapperProxy<T> implements InvocationHandler, Serializable
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
Class<?> declaringClass = method.getDeclaringClass();
return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
private boolean isDefaultMethod(Method method) {
return (method.getModifiers() & 1033) == 1 && method.getDeclaringClass().isInterface();
}
}
-
class MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) { Object param; Object result; switch(this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT: if (this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; } else if (this.method.returnsMany()) { result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) { result = this.executeForCursor(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " +
this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" +
this.command.getName() + " attempted to return null from a method with
a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
private <E> Object executeForMany(SqlSession sqlSession, Object[]
args) {
Object param = this.method.convertArgsToSqlCommandParam(args);
List result;
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectList(this.command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(this.command.getName(), param);
}
if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
} else {
return result;
}
}
mybatis主配置文件
properties使用案例改为
<?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>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1"/>
<property name="username" value="root"/>
<property name="password" value="zheng"/>
</properties>
<environments default="mysql">
<environment id="mysql">
<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>
<mappers>
<mapper resource="com/zheng/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
当然这样配置没有任何屁用,他真正的用法是需要用他的resource属性引入外部的.properties文件如下
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db1
username=root
password=zheng
主配置文件如下
<?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="bd.properties">
</properties>
<environments default="mysql">
<environment id="mysql">
<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>
<mappers>
<mapper resource="com/zheng/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
当然还有url属性URL:Uniform Resources Locator统一资源定位符,他是可以唯一标识一个资源的位置
URI:Uniform Resources Identifier 统一资源标识符他是在应用中唯一定位一个资源
<?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 url="file:///C:/Users/zz/IdeaProjects/mysql/src/main/resources/sqlMapConfig.xml">
</properties>
<environments default="mysql">
<environment id="mysql">
<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>
<mappers>
<mapper resource="com/zheng/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
typeAliases用于设置全限定的别名在主配置文件中,
然而切记主配置文件package是用于设置实体类包的别名,且都不区分大小写,而 <mappers>中映射配置文件中package设置的是DAO的包
<typeAliases>
<!--type是指全限定名,alias是指别名-->
<typeAlias type="com.zheng.User" alias="user"></typeAlias>
</typeAliases>
在主配置中实体类package使用
<typeAliases>
<package name="com.zheng"/>
</typeAliases>
在主配置中映射DAO文件 可以不用写resources或者class代码如下
<mappers>
<package name="com.Dao"></package>
</mappers>
网友评论