栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

mybatis从入门到精通

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

mybatis从入门到精通

一、简单例子

说明:本例基于mybatis-3.2.6。

1.新建web项目mybatis,引入jar包mybatis-3.2.6.jar。

2.新建java类com.hanjun.entity.User(数据库中建立与些类对应的t_user表)

    private int id;
    private String userName;
    private String userAge;
    private String userAddress;

3.在User类同包下新建User.xml


   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 
    
         select * from t_user where id = #{id}
    

 

说明:namespace为自定义的包名,包名.select的id确定唯一sql。parameterType可以不要。

4.在src下新建mybatis配置文件Configuration.xml


   "http://mybatis.org/dtd/mybatis-3-config.dtd">

 
    
 

 
 
    
        
        
            
            
            
            
        

    

 

 
 
    
 

说明:User.xml中的resultType对应此处的alias。如果不声明alias,修改User.xml中的resultType为com.hanjun.entity.User也可

5.测试:

public static void main(String[] args) {
  Reader reader = null;
  try {
      reader = Resources.getResourceAsReader("Configuration.xml");
  } catch (IOException e) {
      e.printStackTrace();
  }
  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  SqlSession session = sqlSessionFactory.openSession();
  try {
      User user = (User) session.selectOne("com.hanjun.entity.User.selectUserByID", 1);
      System.out.println(user.getUserAddress());
      System.out.println(user.getUserName());
  } finally {
      session.close();
  }
}

6.优化,新建com.hanjun.dao.IUserDao,并修改User.xml中mapper的namespace为com.hanjun.dao.IUserDao

public interface IUserDao {
    // 方法名与User.xml中select的id保持一致
    public User selectUserByID(int id);
}

修改测试方法,可以直接使用IUserDao调用数据库了

  SqlSession session = sqlSessionFactory.openSession();
  IUserDao userDao = session.getMapper(IUserDao.class);
  try {
      User user = userDao.selectUserByID(1);
      System.out.println(user.getUserAddress());
      System.out.println(user.getUserName());
  } finally {
      session.close();
  }

二、增删改查

1.增加数据。User.xml中增加

    
         insert into t_user(id,userName,userAge,userAddress)
          values(q_user.nextval,#{userName},#{userAge},#{userAddress}) 
    

说明:如果是MySql等自增主键,insert的属性 seGeneratedKeys设置为"true"表明要MyBatis获取由数据库自动生成的主键;keyProperty="id"指定把获取到的主键值注入到User的id属性

IUserDao中增加

public void addUser(User user);

测试

  User user = new User();
  user.setUserAge("26");
  user.setUserName("luowei");
  user.setUserAddress("湖南");
  userDao.addUser(user);
  session.commit();

说明:增删改操作必须执行session.commit()方法。

2.修改数据。User.xml中增加

    
         update t_user set userName=#{userName},userAge=#{userAge},userAddress=#{userAddress} where id=#{id}
    

IUserDao中增加

public void updateUser(User user);

测试

  User user = userDao.selectUserByID(3);
  user.setUserAge("27");
  userDao.updateUser(user);

3.删除数据。User.xml中增加

    
         delete from t_user where id=#{id}
    

IUserDao中增加

public void deleteUser(User user);

测试

  User user = userDao.selectUserByID(3);
  user.setUserAge("27");
  userDao.deleteUser(user);

4.List查询。User.xml中增加

    
         select u.id userId, u.userName, u.userAddress, a.id, a.title, a.content
           from t_user u, t_article a
          where u.id = a.userid
            and u.id = #{user.id}
    
 

说明:autoMapping设置为true,sql列名和实体类属性名一致时自动注入,可省略配置一些association

说明:resultMapArticle引用resultMapUser也可写为:
     
        
     

测试

  IArticleDao articleDao = session.getMapper(IArticleDao.class);
  User user = new User();
  user.setId(4);
  Article queryVO = new Article();
  queryVO.setUser(user);
  List articles = articleDao.findArticles(queryVO);
  for(Article article : articles){
      System.out.println(article.getTitle()+":"+article.getUser().getUserName());
  }

注:实验结果articles应该返回多条数据时只返回一条,删除association配置就返回正常,换成mybatis-3.1.1.jar后问题解决。后来又换回mybatis-3.2.6,经测试article的id在sql中的别名不能是id,改为aid后问题解决。

6.SQL打印

添加commons-logging-1.1.1.jar log4j-1.2.17.jar src下新建log4j.properties

log4j.logger.com.hanjun.dao=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.Threshold=DEBUG
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%-5p] [%d] %t %c  - %m%n

三、与spring整合

1.加入jar包

commons-dbcp.jar
commons-logging-1.1.1.jar
commons-pool.jar
mybatis-spring-1.2.2.jar
spring-aop-3.2.8.RELEASE.jar
spring-beans-3.2.8.RELEASE.jar
spring-context-3.2.8.RELEASE.jar
spring-core-3.2.8.RELEASE.jar
spring-expression-3.2.8.RELEASE.jar
spring-jdbc-3.2.8.RELEASE.jar
spring-tx-3.2.8.RELEASE.jar

2.src下新建applicationContext.xml


 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="Index of /schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

 
  
  
  
  
 

 
     
     
     
     
  

  
  
     
     
     
     
  

优化:sqlSessionFactory增加     public List getResults() {
       return (List) results;
    }
 
    public void setResults(List results) {
       this.results = results;
    }
}

3.新建com.hanjun.intercept.PageInterceptor.java

@Intercepts( { @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }) })
public class PageInterceptor implements Interceptor {

 private String databaseType;// 数据库类型,不同的数据库有不同的分页方法

 
 public Object intercept(Invocation invocation) throws Throwable {
  // 对于StatementHandler其实只有两个实现类,一个是RoutingStatementHandler,另一个是抽象类baseStatementHandler,
  // baseStatementHandler有三个子类,分别是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,
  // SimpleStatementHandler是用于处理Statement的,PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是
  // 处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个
  // StatementHandler类型的delegate属性,RoutingStatementHandler会依据Statement的不同建立对应的baseStatementHandler,即SimpleStatementHandler、
  // PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。
  // 我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候
  // 是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。
  RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
  // 通过反射获取到当前RoutingStatementHandler对象的delegate属性
  StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
  // 获取到当前StatementHandler的
  // boundSql,这里不管是调用handler.getBoundSql()还是直接调用delegate.getBoundSql()结果是一样的,因为之前已经说过了
  // RoutingStatementHandler实现的所有StatementHandler接口方法里面都是调用的delegate对应的方法。
  BoundSql boundSql = delegate.getBoundSql();
  // 拿到当前绑定Sql的参数对象,就是我们在调用对应的Mapper映射语句时所传入的参数对象
  Object obj = boundSql.getParameterObject();
  // 这里我们简单的通过传入的是Page对象就认定它是需要进行分页操作的。
  if (obj instanceof Page) {
   Page page = (Page) obj;
   // 通过反射获取delegate父类baseStatementHandler的mappedStatement属性
   MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
   // 拦截到的prepare方法参数是一个Connection对象
   Connection connection = (Connection) invocation.getArgs()[0];
   // 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
   String sql = boundSql.getSql();
   // 给当前的page参数对象设置总记录数
   this.setTotalRecord(page, mappedStatement, connection);
   // 获取分页Sql语句
   String pageSql = this.getPageSql(page, sql);
   // 利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
   ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
  }
  return invocation.proceed();
 }

 
 public Object plugin(Object target) {
  return Plugin.wrap(target, this);
 }

 
 public void setProperties(Properties properties) {
  this.databaseType = properties.getProperty("databaseType");
 }

 
 private String getPageSql(Page page, String sql) {
  StringBuffer sqlBuffer = new StringBuffer(sql);
  if ("mysql".equalsIgnoreCase(databaseType)) {
   return getMysqlPageSql(page, sqlBuffer);
  } else if ("oracle".equalsIgnoreCase(databaseType)) {
   return getOraclePageSql(page, sqlBuffer);
  }
  return sqlBuffer.toString();
 }

 
 private String getMysqlPageSql(Page page, StringBuffer sqlBuffer) {
  // 计算第一条记录的位置,Mysql中记录的位置是从0开始的。
  int offset = (page.getPageNo() - 1) * page.getPageSize();
  sqlBuffer.append(" limit ").append(offset).append(",").append(page.getPageSize());
  return sqlBuffer.toString();
 }

 
 private String getOraclePageSql(Page page, StringBuffer sqlBuffer) {
  // 计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的
  int offset = (page.getPageNo() - 1) * page.getPageSize() + 1;
  sqlBuffer.insert(0, "select tt.*, rownum rn from (").append(") tt where rownum <= ").append(offset + page.getPageSize()-1);
  sqlBuffer.insert(0, "select * from (").append(") where rn >= ").append(offset);
  // 上面的Sql语句拼接之后大概是这个样子:
  // select * from (select tt.*, rownum rn from (select * from t_user) tt
  // where rownum <= 30) where rn >= 16
  return sqlBuffer.toString();
 }

 
 private void setTotalRecord(Page page, MappedStatement mappedStatement, Connection connection) {
  // 获取对应的BoundSql,这个BoundSql其实跟我们利用StatementHandler获取到的BoundSql是同一个对象。
  // delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
  BoundSql boundSql = mappedStatement.getBoundSql(page);
  // 获取到我们自己写在Mapper映射语句中对应的Sql语句
  String sql = boundSql.getSql();
  // 通过查询Sql语句获取到对应的计算总记录数的sql语句
  String countSql = this.getCountSql(sql);
  // 通过BoundSql获取对应的参数映射
  List parameterMappings = boundSql.getParameterMappings();
  // 利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
  BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, page);
  // 通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
  ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, page, countBoundSql);
  // 通过connection建立一个countSql对应的PreparedStatement对象。
  PreparedStatement pstmt = null;
  ResultSet rs = null;
  try {
   pstmt = connection.prepareStatement(countSql);
   // 通过parameterHandler给PreparedStatement对象设置参数
   parameterHandler.setParameters(pstmt);
   // 之后就是执行获取总记录数的Sql语句和获取结果了。
   rs = pstmt.executeQuery();
   if (rs.next()) {
    int totalRecord = rs.getInt(1);
    // 给当前的参数page对象设置总记录数
    page.setTotalRecord(totalRecord);
   }
  } catch (SQLException e) {
   e.printStackTrace();
  } finally {
   try {
    if (rs != null)
     rs.close();
    if (pstmt != null)
     pstmt.close();
   } catch (SQLException e) {
    e.printStackTrace();
   }
  }
 }

 
 private String getCountSql(String sql) {
  int index = sql.indexOf("from");
  return "select count(1) " + sql.substring(index);
 }

 public String getDatabaseType() {
  return databaseType;
 }

 public void setDatabaseType(String databaseType) {
  this.databaseType = databaseType;
 }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/343604.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号