JavaEE框架——Mybatis入门和和使用(增删改查,缓存)

一、简介:

  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
  • iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
  • MyBatis 的前身就是 iBatis,iBatis 本是由 ClintonBegin 开发,后来捐给 A pache 基金会,成立了 iBatis 开源项目 2010 5 月该项目由 A pahce 基金会迁移到了 G oogle C ode, 并且改名为 M y B atis 。尽管如此,它的包结构仍然为 ibatis
  • 访问网站
    www.mybatis.org/
    https://github.com/mybatis
  • MyBatis 是一个数据持久层 ( ORM ) 框架。 把实体类和 SQL 语句之间建立了映射关系,是一种半自动化的 ORM 实现。
  • 所有 sql 语句,全部定义在 xml (建议)中。也可以通过注解的方式在接口上实现。这些映射文件称之为 mapper

        

二、MyBatis的优点(与其他架构区别)

  • MyBatis的优点:
    1. 基于SQL语法,简单易学。
    2. 能了解底层组装过程。
    3.   SQL语句封装在配置文件中,便于统一管理与维护,降低了程序的耦合度。
    4. 程序调试方便。
  • 与传统JDBC的比较
    1. 减少了 61% 的代码量
    2. 最简单的持久化框架
    3. 架构级性能增强
    4. SQL 代码从程序代码中彻底分离,可重用
    5. 增强了项目中的分工
    6. 增强了移植性
  •  Hibernate的对比
  • MyBatis和Hibernate比较
     MyBatis

    Hibernate

    是一个SQL语句映射的框架(工具)主流的ORM框架、提供了从POJO到数据库表的全套映射机制
    注重POJO/MapSQL之间的映射关系。不会为程序员在运行期自动生成SQL会自动生成全套SQL语句。
    自动化程度低、手工映射SQL,灵活程度高因为自动化程度高、映射配置复杂,api也相对复杂,灵活性低.
    需要开发人员熟炼掌据SQL语句开发人同不必关注SQL底层语句开发

             

三、架构

1.功能架构讲解:

我们把Mybatis的功能架构分为三层:
  • API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
  • 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
  • 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

2.框架架构讲解:

  • 加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个
    mybatis结构
    个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
  • SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
  • SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
  • 结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。



四、MyBatis基本要素

somename.xml全局配置文件
mapper.xml核心映射文件
SqlSession接口
1.基础配置文件mybatis-config.xml
  • configuration.xml系统的核心配置文件,包含数据源和事务管理器等设置和属性信息,XML文档结构如下

  configuration 配置
  properties 可以配置在 Java 属性配置文件中
 settings 修改 MyBatis 在运行时的行为方式,如是否需要缓存
  typeAliases   Java 类型命名一个短的名字
  typeHandlers 类型处理器-系统默认已经为所有类型设置 OK
  objectFactory 对象工厂 创建 Bean
  plugins 插件  - 拦截 CRUD 操作。
  environments 环境 -配置数据源
 environment 环境变量
 transactionManager 事务管理器  JDBC|JNDI|JTA
 dataSource 数据源
 mappers l   映射器  XML 文件| Mapper | 网络资源


  • 基础配置文件环境配置

        配置环境

<span style="font-size:12px;"><configuration>

<environmentsdefault="development">

<environmentid="development">

<transactionManager type="JDBC"/>

<dataSource type="POOLED">

<propertyname="driver" value="${driver}"/>

<property name="url" value="${url}"/>

<propertyname="username" value="${username}"/>

<propertyname="password" value="${password}"/>

</dataSource>

</environment>

<environmentid="development2">

……

</environment>

</environments>

</configuration></span>

  • 基础配置文件事务管理

MyBatis有两种事务管理类型:

l JDBC- 这个类型直接全部使用 JDBC 的提交和回滚功能。它依靠使用连接的数据源来管理事务的作用域。
l MANAGED- 这个类型什么不做, 它从不提交 、 回滚和关闭连接 。 而是让窗口来管理事务的全部生命周期 。(比如说 Spring 或者 JAVAEE 服务器)

  • 基础配置文件数据源

数据源类型有三种:UNPOOLEDPOOLEDJNDI

  • UNPOOLED- 这个数据源实现只是在每次请求的时候简单的打开和关闭一个连接。虽然这有点慢,但作为一些不需要性能和立即响应的简单应用来说, 不失为一种好选择 。
  • POOLED- 这个数据源缓存 JDBC 连接对象用于避免每次都要连接和生成连接实例而需要的验证时间。对于并发 WEB 应用,这种方式非常流行因为它有最快的响应时间。
  • JNDI- 这个数据源实现是为了准备和 Spring 或应用服务一起使用,可以在外部也可以在内部配置这个数据源,然后在 JNDI 上下文中引用它。这个数据源配置只需要两上属性:
  • 基础配置文件—SQL映射文件
    SQL 映射文件 :

    1.使用相对路径

    <mappers>
    
      <mapper resource="org/mybatis/builder/UserMapper.xml"/>
    
       <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
    
       <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
    
       <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    
    </mappers>

    //2.使用全路径

    <mappers>

      <mapper url="file:///var/sqlmaps/AuthorMapper.xml"/>

      <mapper url="file:///var/sqlmaps/BlogMapper.xml"/>

      <mapper url="file:///var/sqlmaps/PostMapper.xml"/>

    </mappers>


2.SQL映射文件

  • SQL映射文件结构:(有关文件的增删改查结构将用代码来演示)

         

cache-  配置给定命名空间的缓存。
cache-ref–  从其他命名空间引用缓存配置。
resultMap   –  最复杂,也是最有力量的元素,用来描述如何从数据库结果集中来加载对象。 l
sql –  可以重用的 SQL 块,也可以被其他语句引用。
insert–  映射插入语句
update–  映射更新语句
delete–  映射删除语句
select–  映射查询语句
  • 动态SQL
    MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力
    if  
    choose( when,otherwise )
    trim( where,set )
    foreach


五、核心类的生命周期

  • SqlSessionFactoryBuilder的生命周期:这个类可以被初始 、 使用和丢弃 , 如果你已经创建好了一个SqlSessionFactory后就不用再保留它。 因此 ,SqlSessionFactoryBuilder的最好作用域是方法体内,比如说定义一个方法变量。你可以重复使用SqlSessionFactoryBuilder生成多个SqlSessionFactory实例, 但是最好不要强行保留 , 因为 XML 的解析资源要用来做其它更重要的事

  • SqlSession   每个线程都有自己的SqlSession实例,SqlSession实例是不能被共享,也是不是线程安全的。因此最好使用Request作用域或者方法体作用域。不要使用类的静态变量来引用一个SqlSession实例,甚至不要使用类的一个实例变更来引用。如果你正在使用WEB框架,应该让SqlSession跟随HTTP请求的相似作用域。也就是说,在收到一个HTTP请求过后,打开SqlSession,等返回一个回应以后,立马关掉这个SqlSession。关闭SqlSession是非常重要的。你必须要确保SqlSessionfinally方法体中正常关闭。

  • SqlSession  的获取:
    package cn.hncu.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    public class SqlSessionUtil {
    	private static SqlSessionFactory ssf;// 底层对应的是Connection连接池
    	static {
    		InputStream in;
    		try {
    			in = Resources.getResourceAsStream("mybatis-config.xml");// 从classpath位置加载文件,可以使用相对路径如:cn/hncu/mybatis-config.xml
    			SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
    			ssf=ssfb.build(in);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static SqlSessionFactory getSsf() {
    		return ssf;
    	}
    
    	public static SqlSession getSqlSession() {
    		return ssf.openSession();
    	}
    
    	public static void main(String[] args) throws SQLException {
    		//SqlSession是Connection的包装类
    		//每次获取的是不同的SqlSession对象,如果获取一个session对象且从中访问con,则就会到池中去拿一个连接。由于我们在配置文件中设置池大小只有5,因此只有前5个session对象能够顺利取出con对象,第6个session在获取con时会阻塞。
    		//一个session对象在获取con之后如果不使用,那么超过默认时间,它所持有的con连接会被收回池中。这样其它session对象又可以从池中获取
    
    		System.out.println("ssf:"+ssf);
    		//下面这段代码,在5个session获取连接之后会阻塞。过一段时间后,才继续执行6-10个session对象
    		for(int i=1;i<=10;i++){
    			SqlSession ss=getSqlSession();
    			System.out.println(i+":sqlSession:"+ss);//sqlSession不同
    			Connection con=ss.getConnection();
    			System.out.println(i+":con:"+con);//Connection相同
    			ss.close();//session对象关闭之后,它所持有的con会还回池中,被下一个session重新持有
    		}
    	}
    }
    

    mybatis-config.xml的配置是基础的配置




六、基本使用演示:

##使用前准备:
1).导入log4j-1.2.16.jar,mybatis-3.4.1.jar和mysql-connector-java-5.1.34-bin.jar包
2).在src目录下导入或编写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>
    <typeAliases>
        <typeAlias alias="User" type="cn.hncu.domain.User" />
        <typeAlias alias="Person" type="cn.hncu.domain.Person" />
        <!-- <typeAlias alias="User" type="cn.hncu.domain.UserMapper" /> 如果用namespace不行-->
        
        
    </typeAliases>
    <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:///mybatis?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="1234" />
                <property name="poolMaximumActiveConnections" value="5" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="cn/hncu/domain/User.xml" />
        <mapper resource="cn/hncu/domain/User2.xml" />
        <mapper resource="cn/hncu/domain/Person.xml" />
    </mappers>

</configuration>

3).写SqlSessionUtil连接池工具。(用于拿连接)
package cn.hncu.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SqlSessionUtil {
	private static SqlSessionFactory ssf;// 底层对应的是Connection连接池
	static {
		InputStream in;
		try {
			in = Resources.getResourceAsStream("mybatis-config.xml");// 从classpath位置加载文件,可以使用相对路径如:cn/hncu/mybatis-config.xml
			SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
			ssf=ssfb.build(in);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static SqlSessionFactory getSsf() {
		return ssf;
	}

	public static SqlSession getSqlSession() {
		return ssf.openSession();
	}

	public static void main(String[] args) throws SQLException {
		//SqlSession是Connection的包装类
		//每次获取的是不同的SqlSession对象,如果获取一个session对象且从中访问con,则就会到池中去拿一个连接。由于我们在配置文件中设置池大小只有5,因此只有前5个session对象能够顺利取出con对象,第6个session在获取con时会阻塞。
		//一个session对象在获取con之后如果不使用,那么超过默认时间,它所持有的con连接会被收回池中。这样其它session对象又可以从池中获取

		System.out.println("ssf:"+ssf);
		//下面这段代码,在5个session获取连接之后会阻塞。过一段时间后,才继续执行6-10个session对象
		for(int i=1;i<=10;i++){
			SqlSession ss=getSqlSession();
			System.out.println(i+":sqlSession:"+ss);//sqlSession不同
			Connection con=ss.getConnection();
			System.out.println(i+":con:"+con);//Connection相同
			ss.close();//session对象关闭之后,它所持有的con会还回池中,被下一个session重新持有
		}
	}
}

4)在mybatis数据库中建立表格(利用ant工具)
demo.sql
create database if not exists mybatis default character set 'utf8';

use mybatis;

create table users(
  id varchar(32) primary key,
  name varchar(32),
  pwd varchar(32)
);
insert into users value('U001','Jack','1234');
insert into users value('U010','张三','4321');




CREATE TABLE cpu(
   id INT AUTO_INCREMENT PRIMARY KEY,
   name VARCHAR(30)
);





CREATE TABLE persons(
   pid VARCHAR(32) PRIMARY KEY,
   pname VARCHAR(30)
);
CREATE TABLE cars(
   id VARCHAR(32) PRIMARY KEY,
   NAME VARCHAR(30),
   price NUMERIC(10,2),
   pid VARCHAR(32),
   CONSTRAINT car_fk FOREIGN KEY(pid) REFERENCES persons(pid)
);
INSERT INTO persons VALUES('P001','Jack');
INSERT INTO persons VALUES('P002','Rose');
INSERT INTO persons VALUES('P003','张三');
INSERT INTO cars VALUES('C001','BMW',100,'P001');
INSERT INTO cars VALUES('C002','BenZ',80,'P001');
INSERT INTO cars VALUES('C003','Jeep',50,'P003');



CREATE TABLE card(
   card_id VARCHAR(32) PRIMARY KEY,
   card_gov VARCHAR(30),
   pid VARCHAR(32) unique,
   CONSTRAINT card_fk FOREIGN KEY(pid) REFERENCES persons(pid)
);
INSERT INTO card VALUES('C001','北京市公安局','P001');
INSERT INTO card VALUES('C002','长沙市公安局','P002');


CREATE TABLE roles(
   id VARCHAR(32) PRIMARY KEY,
   name VARCHAR(30),
);
CREATE TABLE roleuser(
   roleid VARCHAR(32),
   userid VARCHAR(32),
   CONSTRAINT ru_pk PRIMARY KEY(roleid,userid),
   CONSTRAINT ru_fk1 FOREIGN KEY(roleid) 
                                   REFERENCES roles(id),
   CONSTRAINT ru_fk1 FOREIGN KEY(userid) 
                                   REFERENCES users(id)
);

INSERT INTO roles VALUES('R001','教师');
INSERT INTO roles VALUES('R002','老板');
INSERT INTO roles VALUES('R003','学生');

INSERT INTO roleuser VALUES('R001','U001');
INSERT INTO roleuser VALUES('R002','U001');
INSERT INTO roleuser VALUES('R003','U001');

INSERT INTO roleuser VALUES('R001','U010');
INSERT INTO roleuser VALUES('R001','U002');
INSERT INTO roleuser VALUES('R002','U003');
INSERT INTO roleuser VALUES('R002','U003');



 
5)使用。(建立javaBean)



1.查询:

1)查询学生信息
   映射文件
<!--查询的返回结果是一个集合(List),resultType属性设置的是集合中一个元素的类型-->
	<select id="query1" resultType="User" ><!--用别名来设置返回类型-->
		select * from users
	</select>

测试代码:
@Test//查询学生信息
	public void query1(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		List<User> list=s.selectList("query1");
		System.out.println(list);
		s.close();//执行之后,最好养成关session的习惯
	}
2)查询学生信息(接口方式---推荐方式)
   映射文件
  <!--采用接口的方式访问时,命名空间得取成“接口全名” -->
<mapper namespace="cn.hncu.domain.UserMapper">
    <!--查询的返回结果是一个集合(List),resultType属性设置的是集合中一个元素的类型-->
    <select id="query1" resultType="User" ><!--用别名来设置返回类型-->
        select * from users
    </select>
</mapper>
接口代码
package cn.hncu.domain;

import java.util.List;

public interface UserMapper {
	public List<User> query1();


	public List<User> queryByID(String string);
}

  测试代码
@Test//采用接口的方式访问----mybatis推荐的访问方式
	public void queryByInterface(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		//与上一版本不同之处---通过接口访问
		UserMapper up=s.getMapper(UserMapper.class);
		List<User> list=up.query1();
		System.out.println(list);
		s.close();
}

3)查询学生信息(带单个条件)

映射文件
<!--带条件的查询1:单个条件   助理解:类型方法中的形参:String val -->
    <select id="queryByID" resultType="User" parameterType="string">
        select * from users where 1=1 and id=#{val}
        <!-- <if test="id!=null">
            and id=#{val}
        </if> 不行-->
    </select>
测试代码
@Test//带条件的查询1:单个条件
	public void queryByID(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		//与上一版本不同之处---通过接口访问
		List<User> list=s.selectList("queryByID", "U001");
		System.out.println(list);
		s.close();
	}

4)带多条件的查询2:多个条件
映射文件
<!--带条件的查询2:多个条件   注意: sql语句中的参数名必须和值对象中的属性名一致 -->
	<select id="queryUser" resultType="User" parameterType="User">
		select * from users where 1=1 and id=#{id}
		<if test="name!=null">
			and name like #{name}
		</if>
	</select>

测试代码
@Test//带多条件的查询2:多个条件
	public void queryUser(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		User user=new User();
		user.setId("U001");
		user.setName("Jack");
		List<User> list=s.selectList("queryUser",user);
		System.out.println(list);
	}

5)返回结果封装成List<Map>的查询
映射文件
<select id="queryTOList" resultType="hashmap" parameterType="User">
        select * from users
    </select>
测试代码
@Test//封装成List<map>
	public void queryTOList(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		List<User> list=s.selectList("queryTOList");
		System.out.println(list);
	}

6)返回结果封装成List<Map>的查询  ,条件为hashmap,like查询
映射文件
<!--  返回结果封装成List<Map>的查询  ,条件为hashmap-->
	<select id="queryTOList2" resultType="hashmap" parameterType="hashmap">
		select * from users where 1=1 
		<if test="id!=null">
			and id=#{id}
		</if>
		<if test="name!=null">
			and name like CONCAT('%','${name}','%' ) 
		</if>
</select>
测试代码
@Test//封装成List<map> @Test//查询的输入参数为:HashMap
	public void queryTOList2(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		Map<String, Object> map = new HashMap<String, Object>();
//		map.put("id", "U003");
		map.put("name", "J");
		List<User> list=s.selectList("queryTOList2",map);
		System.out.println(list);
	}

2.增删改:(一定要提交哦)

1).增:
映射文件
@Test//insert(插入)
    public void insert(){
        SqlSession s=SqlSessionUtil.getSqlSession();
        User user=new User();
        user.setId("U005");
        user.setName("BGW");
        user.setPwd("55555");
        s.insert("insert",user);
        s.commit();//必须显示提交。因为mybatis默认是带事务的即不自动提交
        s.close();
    }

测试代码
  
<insert id="insert" parameterType="User">
		insert into users(id,name,pwd) values(#{id},#{name},#{pwd})
	</insert>

2)单
映射文件
<delete id="delete" parameterType="hashmap">
		delete from users where 1=1 
		<if test="id!=null">
			and id=#{id}
		</if>
		<if test="name!=null">
			and name=#{name}
		</if>
	</delete>
测试代码
@Test//delete删除
	public void delete(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("id", "U005");
		map.put("name", "Jack");
		s.delete("delete", map);
		s.commit();//必须显示提交。因为mybatis默认是带事务的即不自动提交
		s.close();
	}

3)更新(修改)
映射文件
<update id="update" parameterType="User">
		update users set id=#{id}
		<if test="name!=null">
			, name=#{name} 
		</if>
		<if test="pwd!=null">
			,pwd=#{pwd} 
		</if>
			where id=#{id}
	</update>

测试代码
	@Test//update更新
	public void update(){
		SqlSession s=SqlSessionUtil.getSqlSession();
		Map<String, Object> map = new HashMap<String, Object>();
		User user=new User();
		user.setId("U005");
		user.setName("大哥王");
		s.update("update",user);
		s.commit();//必须显示提交。因为mybatis默认是带事务的即不自动提交
		s.close();
	}



   4)用mybatis调用底层jdbc实现自动生成主键
  映射文件
<!-- 用mybatis调用底层jdbc实现自动生成主键 -->
    <insert id="generateKey2" parameterType="hashmap"
        keyProperty="id"
        keyColumn="id"
        useGeneratedKeys="true"
    >
        insert into cpu(name) values(#{name})
    </insert>
测试代码

//mybatis中实现自动生成主键
	@Test//底层jdbc的方式
	public void generateKey1() throws SQLException{
		SqlSession s=SqlSessionUtil.getSqlSession();
		Connection con=s.getConnection();
		PreparedStatement pst=con.prepareStatement("insert into cpu(name) values(?)",Statement.RETURN_GENERATED_KEYS);
		pst.setString(1, "ibm");
		int a=pst.executeUpdate();
		ResultSet rs=pst.getGeneratedKeys();
		rs.next();
		s.commit();
		s.close();
		System.out.println(rs.getInt(1));
	}
	@Test//mybatis的方式
	public void generateKey2() throws SQLException{
		SqlSession s=SqlSessionUtil.getSqlSession();
		Map<String, Object> map=new HashMap<String, Object>();
		map.put("name", "intel");
		int n=s.insert("generateKey2",map);
		System.out.println(n); //n是影响的行数
		System.out.println(map.get("id"));
		s.commit();
		s.close();
	}






3.复杂操作和标签的操作(m命名空间为user,基础配置文件是原来的)

1)多条件查询
   映射文件

	<select id="dyncUser1" resultType="User" parameterType="User">
	select * from users where 1=1
		<if test="id!=null">
			and id=#{id}
		</if>
		<if test="pwd!=null">
			and pwd like #{pwd}
		</if>
		<if test="name!=null">
			and name like #{name}
		</if>
	</select>
2)where标签的使用
映射文件
<select id="dyncUser2" resultType="User" parameterType="User">
		select * from users
		<!-- 用where标签代替上一版本的: where 1=1 -->
		<where>
			<if test="id!=null">
				and id=#{id}<!-- 解析时第一个and会去掉 -->
			</if>
			<if test="pwd!=null">
				and pwd like #{pwd}
			</if>
			<if test="name!=null">
				and name like #{name}
			</if>
		</where>
	</select>
3)trim标签的使用
映射文件
<!-- trim的使用 -->
	<select id="dyncUser3" resultType="User" parameterType="User">
		select * from users
		<!-- 用前缀“where”去覆盖第一个子句中的“and”或“or” -->
		<trim prefix="where" prefixOverrides="and|or">
			<if test="id!=null">
				and id=#{id}
			</if>
			<if test="pwd!=null">
				and pwd like #{pwd}
			</if>
			<if test="name!=null">
				and name like #{name}
			</if>
		</trim>
	</select>
4)set标签的使用
映射文件  
<!-- 使用set标签进行更新记录 -->
	<update id="dyncUpdate" parameterType="User">
		update users
		<set>
			<if test="name!=null">
				name=#{name}
			</if>
			<if test="pwd!=null">
				pwd=#{pwd}
			</if>
		</set>
		where id=#{id}
	</update>

5)foreach标签的使用
映射文件
 
<!-- for each执行的效果: ('U001','U002','U010') 相间查询 -->
	<select id="dyncUser4" resultType="User" parameterType="list">
		select * from users
		<if test="list!=null and list.size()>0">
			 where id in
			<foreach collection="list" index="idx" item="val" open="("
				close=")" separator=",">
				#{val}
			</foreach>
		</if>
	</select>
测试代码
package cn.hncu.demo;

import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import cn.hncu.domain.User;
import cn.hncu.utils.SqlSessionUtil;

public class Demo2 {
	@Test
	public void query(){
		SqlSession s = SqlSessionUtil.getSqlSession();
		User u = new User();
//		u.setId("U001");
		u.setName("%Jack%");
//		u.setPwd("%1%");
//		List<User> list=s.selectList("user.dyncUser1", u);
//		List<User> list=s.selectList("user.dyncUser2", u);
		List<User> list=s.selectList("user.dyncUser3", u);
		System.out.println(list);
	}
	@Test
	public void update(){
		SqlSession s = SqlSessionUtil.getSqlSession();
		User u = new User();
		u.setId("U005");
		u.setName("Tom");
//		u.setPwd("1565");
		s.update("dyncUpdate",u);
		s.commit();
		s.close();
	}
	@Test
	public void queryForeach(){
		SqlSession s = SqlSessionUtil.getSqlSession();
		List<Object> list=new ArrayList<Object>();
		list.add("U001");
		list.add("U005");
		list=s.selectList("dyncUser4", list);
		System.out.println(list);
		s.close();
	}
}




5.多表操作(namespace="person")

1)一对多查询操作
法1映射文件(内连接)
<!-- 一对多:法1 -->
	<!--用resultMap来定义一个:自定义复杂结果类型 。注意,column指的是查询结果集中的字段别名-->
	<resultMap type="Person" id="ps"><!-- 这里的colum都是查询出来的表的column -->
		<id property="id" column="pid"/>
		<result property="name" column="pname"/>
		<collection property="cars" javaType="cn.hncu.domain.Car">
			<id property="id" column="cid"/>
			<result property="name" column="cname" javaType="string" jdbcType="VARCHAR"/>
			<result property="price" column="cprice" javaType="double" jdbcType="NUMERIC"/>
		</collection>
	</resultMap>
	<!-- resultType只能用于定义简单类型。要通过resultMap来定义一个:自定义复杂结果类型  -->
	<!-- 此处是引用上面定义的复杂结果类型  -->
	<select id="person1" resultMap="ps">
		select p.pid as pid,p.pname as pname,c.id as cid,c.name as cname
		 from persons p inner join cars c on p.pid=c.pid
	</select>


法2映射文件(左外连接)
<!-- 一对多:法2 (效果为left join),子查询或嵌套查询 -->
	<select id="car1" resultType="cn.hncu.domain.Car" parameterType="string">
		select *from cars where pid=#{pid}
	</select>
	<resultMap type="Person" id="ps2">
		<id column="pid" property="id"/>
		<result property="name" column="pname"/>
		<collection property="cars" select="car1" column="pid"></collection>
	</resultMap>
	<select id="person2" resultMap="ps2">
		select pid,pname from persons <!-- 这里的pname结果无效 -->
	</select>



测试文件
//内联: 查询哪些人有哪些车
	@Test
	public void query(){
		SqlSession s = SqlSessionUtil.getSqlSession();
//		List<Person> persons = s.selectList("person.person1");
		List<Person> persons = s.selectList("person.person2");
		System.out.println(persons);
		s.close();
	}


2.一对一的多表查询
映射文件
<!-- 以下演示一对一关系  -->
	<!-- 同理,下面字段名都是别名 -->
	
	<resultMap type="cn.hncu.domain.Card" id="c1">
		<!-- <id property="id" column="id"/> 可以-->
		<constructor>
			<idArg  column="id" javaType="string"/>
		</constructor>
		<result property="gov" column="gov" javaType="string" jdbcType="VARCHAR"/>
		<association property="person"  javaType="Person">
				<!-- mybatis建议使用id,这里只是展示用result也可以,但性能有差别 -->
				<id property="id" column="pid"/>
				<result property="name" column="pname"/>
				<collection property="cars" select="car1" column="pid">	</collection>
		</association>
	</resultMap>
	<select id="card" resultMap="c1">
		SELECT c.card_id AS id,c.card_gov AS gov ,p.pid AS pid,p.pname AS pname 
        FROM card c INNER JOIN  persons p ON  c.pid = p.pid
	</select>
测试文件
  一对一   
@Test
public void queryCard(){
	SqlSession s = SqlSessionUtil.getSqlSession();
	List<Card> cards = s.selectList("person.card");
	System.out.println(cards);
	s.close();
}
3.多对多的多表查询
映射文件
<mapper namespace="role">
       <!-- 多对多演示 -->
	<resultMap type="User" id="ms" >
		<id property="id" column="uid"/>
		<result property="name" column="uname"/>
		<result property="pwd" column="pwd"/>
		<collection property="roles" javaType="cn.hncu.domain.Role">
			<id property="id" column="uid"/>
			<result property="name" column="关系"/>
		</collection>
	
	</resultMap>
    
	<select id="mutilSelect" resultMap="ms">
		SELECT u.id AS uid, u.name AS uname,u.pwd AS pwd,r.id AS rid,r.name AS 关系 
		FROM users u INNER JOIN roleuser ru ON u.id=ru.userid 
		INNER JOIN roles r ON r.id=ru.roleid
	</select>
		
</mapper>

测试代码
/  多对多   
	@Test
	public void mutilSelect(){
		SqlSession s = SqlSessionUtil.getSqlSession();
		List<User> user = s.selectList("role.mutilSelect");
		System.out.println(user);
		s.close();
	}





5.其他操作

1)sql代码片段(include标签的使用)
<!-- sql片段/ -->
	<sql id="sql1">
		id,name,pwd
	</sql>
	<select id="queryBySql" resultType="User">
		select <include refid="sql1"/> from users
</select>

2)缓存机制:mybatis和hibernate一样,有两级缓存,第一级是session级别的缓存,第二级是二级缓存

  一级缓存演示:
    
<select id="one" parameterType="string" resultType="cn.hncu.domain.Role" >
		select *from roles where id=#{id}
	</select>

测试代码:
@Test //mybatis和hibernate一样,一级缓存是一定存在的,即同一个session对象同享同一个缓存,因此多次获取相同的对象(内存地址相同即是同一个对象)
	public void cacheDemo(){
		SqlSession s = SqlSessionUtil.getSqlSession();
		Role r1=s.selectOne("one", "R001");
		System.out.println(r1.hashCode());
		Role r2=s.selectOne("one", "R001");
		System.out.println(r2.hashCode());
		s.close();
	}
结果:两个r的hashCode一样是session级别的缓存


二级缓存演示:
映射设置
  • 在mybatis-config.xml里设置
<settings>
		<setting name="cacheEnabled" value="true"/>
	</settings>

  • 在相应的映射文件设置
<!-- 配置缓存(二级)步骤2 -->
	<cache></cache>
	
	<select id="one" parameterType="string" resultType="cn.hncu.domain.Role" >
		select *from roles where id=#{id}
	</select>
  • 值对象实现Serializable接口

测试:

@Test //mybatis和hibernate一样,二级缓存需要配置,即两个session对象都从同一个二级缓存中把同一个对象clone到本地一级缓存,因此两个session获取的相同对象的内存地址仍不同,但可以观察到访问数据库只有一次!
	public void cacheDemo2(){
		SqlSession s1 = SqlSessionUtil.getSqlSession();
		Role r1=s1.selectOne("one", "R001");
		System.out.println("r1"+r1.hashCode());
		s1.close();//两个session写在一起同时执行,要先把第一个执行完(close),第二级才能从二级缓存中读取出来有,我的理解为有事务隔离
		SqlSession s2 = SqlSessionUtil.getSqlSession();
		Role r2=s2.selectOne("one", "R001");
		System.out.println("r2"+r2.hashCode());
		s2.close();
	}

由于事务的隔离性质,要把第一个SqlSession提交后才可以进行第二个输出


















   

     







  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个可能的实现思路和代码示例。请注意,这只是一个粗略的示例,您需要根据具体需求进行修改和优化。 1. 框架搭建 首先,您需要搭建JSP+servlet+Mybatis框架,这里提供一个简单的搭建步骤: 1.1 安装Tomcat服务器和MySQL数据库,并启动Tomcat服务器和MySQL数据库。 1.2 在Eclipse等IDE中新建一个Dynamic Web Project项目,选择Tomcat服务器。 1.3 在项目中添加必要的JSP、servlet和Mybatis框架的jar包。 1.4 编写web.xml文件,配置servlet和servlet-mapping。 2. 管理员注册登录功能 2.1 创建管理员表 首先,您需要创建一个管理员表,用于存储管理员的信息。可以使用以下语句在MySQL数据库中创建一个管理员表: ```sql CREATE TABLE `admin` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` 2.2 注册功能实现 在注册页面中,用户需要输入用户名和密码,然后点击注册按钮。在后台,可以使用servlet接收表单数据,然后使用Mybatis框架将数据插入到数据库中。 ```java public class RegisterServlet extends HttpServlet { private static final long serialVersionUID = 1L; private AdminDao adminDao = new AdminDaoImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); Admin admin = new Admin(); admin.setUsername(username); admin.setPassword(password); boolean result = adminDao.addAdmin(admin); if(result) { response.sendRedirect("login.jsp"); } else { response.sendRedirect("register.jsp"); } } } ``` 2.3 登录功能实现 在登录页面中,用户需要输入用户名和密码,然后点击登录按钮。在后台,可以使用servlet接收表单数据,然后使用Mybatis框架查询数据库中是否存在对应的管理员信息。 ```java public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private AdminDao adminDao = new AdminDaoImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); Admin admin = adminDao.getAdminByUsername(username); if(admin != null && admin.getPassword().equals(password)) { HttpSession session = request.getSession(); session.setAttribute("admin", admin); response.sendRedirect("index.jsp"); } else { response.sendRedirect("login.jsp"); } } } ``` 3. 产品信息增删改查 3.1 创建产品表 首先,您需要创建一个产品表,用于存储产品的信息。可以使用以下语句在MySQL数据库中创建一个产品表: ```sql CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `price` double NOT NULL, `description` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` 3.2 增加产品信息 在增加产品信息页面中,用户需要输入产品名称、价格和描述,然后点击提交按钮。在后台,可以使用servlet接收表单数据,然后使用Mybatis框架将数据插入到数据库中。 ```java public class AddProductServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ProductDao productDao = new ProductDaoImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); double price = Double.parseDouble(request.getParameter("price")); String description = request.getParameter("description"); Product product = new Product(); product.setName(name); product.setPrice(price); product.setDescription(description); boolean result = productDao.addProduct(product); if(result) { response.sendRedirect("productList.jsp"); } else { response.sendRedirect("addProduct.jsp"); } } } ``` 3.3 删除产品信息 在删除产品信息页面中,用户需要选择需要删除的产品,然后点击删除按钮。在后台,可以使用servlet接收表单数据,然后使用Mybatis框架从数据库中删除对应的产品信息。 ```java public class DeleteProductServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ProductDao productDao = new ProductDaoImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int id = Integer.parseInt(request.getParameter("id")); boolean result = productDao.deleteProductById(id); if(result) { response.sendRedirect("productList.jsp"); } else { response.sendRedirect("productList.jsp"); } } } ``` 3.4 修改产品信息 在修改产品信息页面中,用户需要选择需要修改的产品,然后输入修改后的产品信息,最后点击修改按钮。在后台,可以使用servlet接收表单数据,然后使用Mybatis框架更新数据库中对应的产品信息。 ```java public class UpdateProductServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ProductDao productDao = new ProductDaoImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int id = Integer.parseInt(request.getParameter("id")); String name = request.getParameter("name"); double price = Double.parseDouble(request.getParameter("price")); String description = request.getParameter("description"); Product product = new Product(); product.setId(id); product.setName(name); product.setPrice(price); product.setDescription(description); boolean result = productDao.updateProduct(product); if(result) { response.sendRedirect("productList.jsp"); } else { response.sendRedirect("updateProduct.jsp?id=" + id); } } } ``` 3.5 查询产品信息 在查询产品信息页面中,用户可以选择需要查询的产品,然后点击查询按钮。在后台,可以使用Mybatis框架查询数据库中对应的产品信息,并将结果返回到前台页面显示。 ```java public class QueryProductServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ProductDao productDao = new ProductDaoImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); List<Product> productList = productDao.getProductListByName(name); request.setAttribute("productList", productList); request.getRequestDispatcher("productList.jsp").forward(request, response); } } ``` 以上是一个简单的示例,希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值