MyBatis 关联查询--逆向工程--实训2019/10/30

4 关联映射

一对一 人 +IdCard

在这里插入图片描述

public class Student {
	private Integer id;
	private String name;
	private Card card;//关联属性

StudentMapper.xml
<mapper namespace="studentNamespace">
	
	 <resultMap type="cn.atcast.javaee.mybatis.one2one.Student" id="studentMap">
	 	<id property="id" column="sid"></id>
	 	<result property="name" column="sname"></result>
	 	 <!-- association :关联 -->
	 	<association property="card" resultMap="cardNamespace.cardMap"></association>
	 </resultMap>
	 
	
	 <!-- 查询1号学生的信息 -->
	 <select id="findById" parameterType="int" resultMap="studentMap">
	 	select  s.sid,s.sname,c.cid,c.cnum
		from students s INNER JOIN cards c
		on s.scid=c.cid
		where s.sid=#{id}
	 </select>
	 
	  <!-- 按学生名查询 -->
	 <select id="findByName" parameterType="string" resultMap="studentMap">
	 	select  s.sid,s.sname,c.cid,c.cnum
		from students s INNER JOIN cards c
		on s.scid=c.cid
		where s.sname=#{name}
	 </select>
</mapper>





public class Card {
	private Integer id;
	private String num;
	private Student student; //双向


CardMapper.xml

<?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="cardNamespace">
	<resultMap type="cn.atcast.javaee.mybatis.one2one.Card" id="cardMap">
		<id property="id" column="cid"/>
		<result property="num" column="cnum"></result>
	</resultMap>
</mapper>

/**
*工具类MybatisUtil
*/
首先回顾一下我们使用Mybatis来操作数据库的步骤:

1、加载核心配置文件sqlMapConfig.xml(名字可以自己随便取);

2、通过 SqlSessionFactoryBuilder来得到SqlSessionFactory ;

3、通过SqlSessionFactory 来创建SqlSession;

4、通过SqlSession来调用增删该查的方法或者获取Mapper对象来调用相应的方法去操作数据库;

5、提交事务;

6、关闭SqlSession。

但是每次操作数据库都要重复上述步骤,为什么不像封装jdbcUtil一样封装一个工具类呢?

import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
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 MybatisUtil {
    // 将SqlSession创建出来,存放到Threadlocal中,需要时再从中取出
	private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
	private static SqlSessionFactory sqlSessionFactory;
	/**
	 * 加载位于src/mybatis.xml配置文件
	 */
	static{
		try {
		          // InputStream 对象接收也可以
			Reader reader = Resources.getResourceAsReader("mybatis.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	/**
	 * 禁止外界通过new方法创建 
     *初始化sqlSessionFactory,只需要一个对象,所以是静态的
     */

	private MybatisUtil(){}
	/**
	 * 获取SqlSession
	 */
	public static SqlSession getSqlSession(){
		//从当前线程中获取SqlSession对象
		SqlSession sqlSession = threadLocal.get();
		//如果SqlSession对象为空
		if(sqlSession == null){
			//在SqlSessionFactory非空的情况下,获取SqlSession对象
			sqlSession = sqlSessionFactory.openSession();
			//将SqlSession对象与当前线程绑定在一起
			threadLocal.set(sqlSession);
		}
		//返回SqlSession对象
		return sqlSession;
	}
	/**
	 * 关闭SqlSession与当前线程分开
	 */
	public static void closeSqlSession(){
		//从当前线程中获取SqlSession对象
		SqlSession sqlSession = threadLocal.get();
		//如果SqlSession对象非空
		if(sqlSession != null){
			//关闭SqlSession对象
			sqlSession.close();
			//分开当前线程与SqlSession对象的关系,目的是让GC尽早回收
			threadLocal.remove();//threadLocal.set(null);
		}
	}
	
	
	
	/**
	 * 测试
	 */
	public static void main(String[] args) {
		Connection conn = MybatisUtil.getSqlSession().getConnection();
		System.out.println(conn!=null?"连接成功":"连接失败");
	}
}

测试类

public class StudentCardDao {
	/**
	 * 查询1号学生的信息
	 * @param id 表示学生的编号
	 */
	public Student findById(int id) throws Exception{
		SqlSession sqlSession = null;
		try{
			sqlSession = MybatisUtil.getSqlSession();
			return sqlSession.selectOne("studentNamespace.findById",id);
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			MybatisUtil.closeSqlSession();
		}
	}
	/**
	 * 查询"哈哈"学生的信息
	 * @param name 表示学生的姓名
	 */
	public Student findByName(String name) throws Exception{
		SqlSession sqlSession = null;
		try{
			sqlSession = MybatisUtil.getSqlSession();
			return sqlSession.selectOne("studentNamespace.findByName",name);
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			MybatisUtil.closeSqlSession();
		}
	}
	
	public static void main(String[] args) throws Exception{
		StudentCardDao dao = new StudentCardDao();
		Student s = dao.findById(1);
		System.out.println(s.getId()+":"+s.getName()+":"+s.getCard().getId()+":"+s.getCard().getNum());
		
		System.out.println("-------------------------------");
		
		s = dao.findByName("哈哈");
		System.out.println(s.getName()+"的身份证号码为:" + s.getCard().getNum());
	}
}

4.2 一对多映射【班级与学生】

在这里插入图片描述

/**

  • 学科(单方)
    */

public class Grade {
	private Integer id;
	private String name;
	//学科就是系
	private List<Student> studentList = new ArrayList<Student>();//关联属性
	public Grade(){}
..set/get省略
}


GradeMapper.xml

<?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="gradeNamespace">
	<resultMap type="cn.atcast.javaee.mybatis.one2many.Grade" id="gradeMap">
		<id property="id" column="gid"></id>
		<result property="name" column="gname"></result>
	<!-- 	<collection property="studentList" resultMap="studentNamespace.studentMap"></collection> 不写也可以拿到-->
	</resultMap>
	 
	 <!-- 根据用户名去查班级 -->
	<select id="findByName" parameterType="string" resultMap="gradeMap">
		select g.gname
		from students s INNER JOIN  grades g
		on s.sgid=g.gid
		and s.sname=#{name}
	</select>
	
</mapper>

/**

  • 学生(多方)
    */
public class Student {
	private Integer id;
	private String name;
	private Grade grade;//关联属性
	public Student(){}
..set/get省略
}




StudentMapper.xml
<?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="studentNamespace">
	 <resultMap type="cn.atcast.javaee.mybatis.one2many.Student" id="studentMap">
	 	<id property="id" column="sid"></id>
	 	<result property="name" column="sname"></result>
	 	<association property="grade" resultMap="gradeNamespace.gradeMap"></association>
	 </resultMap>
	
	 
	<select id="findAllByName" parameterType="string" resultMap="studentMap">
		select s.sid,s.sname
		from students s INNER JOIN  grades g
		on s.sgid=g.gid
		and g.gname=#{name}
	</select>
</mapper>

/** 测试

  • {持久层}
    */
public class GradeStudentDao {
	/**
	 * 查询java学科有哪些学生信息
	 * @param name 表示学科名
	 */
	public List<Student> findAllByName(String name) throws Exception{
		SqlSession sqlSession = null;
		try{
			sqlSession = MybatisUtil.getSqlSession();
			return sqlSession.selectList("studentNamespace.findAllByName",name);
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			MybatisUtil.closeSqlSession();
		}
	}
	
	/**
	 * 查询哈哈是哪个学科的
	 * @param name 表示学生姓名
	 */
	public Grade findByName(String name) throws Exception{
		SqlSession sqlSession = null;
		try{
			sqlSession = MybatisUtil.getSqlSession();
			return sqlSession.selectOne("gradeNamespace.findByName",name);
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			MybatisUtil.closeSqlSession();
		}
	}
	
	public static void main(String[] args) throws Exception{
 		GradeStudentDao dao = new GradeStudentDao();
		List<Student> studentList = dao.findAllByName("java");
		System.out.println("java学科有"+studentList.size()+"个学生,它们信息如下:");
		for(Student s : studentList){
			System.out.println(s.getId()+":"+s.getName());
		}
		System.out.println("-----------------------------------------------------------");
		Grade grade = dao.findByName("哈哈");
		System.out.println("哈哈是"+grade.getName()+"学科的");
		System.out.println("-----------------------------------------------------------");
		grade = dao.findByName("呵呵");
		System.out.println("呵呵是"+grade.getName()+"学科的");
	}
}

4.3 多对多映射【学生与课程】在这里插入图片描述

/**

  • 学生(多方)
    */
public class Student {
	private Integer id;
	private String name;
	private List<Course> courseList = new ArrayList<Course>();//关联属性
	public Student(){}

StudentMapper.xml

<?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="studentNamespace">
	<resultMap type="cn.atcast.javaee.mybatis.many2many.Student" id="studentMap">
		<id property="id" column="sid"/>
		<result property="name" column="sname"/>
	</resultMap>	
	<select id="findAllByCourseName" parameterType="string" resultMap="studentMap">
		select s.sname
		from students s inner join middles m
		on s.sid = m.msid 
		inner join courses c
		on m.mcid = c.cid
		and c.cname = #{name}
	</select>
</mapper>

/**

  • 课程(多方)
    */
public class Course {
	private Integer id;
	private String name;
	private List<Student> studentList = new ArrayList<Student>();//关联属性
	public Course(){}

CourseMapper.xml

 <?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="courseNamespace">
	<resultMap type="cn.atcast.javaee.mybatis.many2many.Course" id="courseMap">
		<id property="id" column="cid"/>
		<result property="name" column="cname"/>
	</resultMap>	
	
	<!-- 查询哈哈选学了哪些课程 -->
	<select id="findAllByName" parameterType="string" resultMap="courseMap">
		select c.cid,c.cname
		from students s inner join middles m
		on s.sid = m.msid
		inner join courses c
		on m.mcid = c.cid
		and s.sname = #{name}
	</select>
	
</mapper>

StudentCourseDao.java

public class StudentCourseDao {
	/**
	 * 查询哈哈选学了哪些课程
	 * @param name 表示学生的姓名
	 */
	public List<Course> findAllByName(String name) throws Exception{
		SqlSession sqlSession = null;
		try{
			sqlSession = MybatisUtil.getSqlSession();
			return sqlSession.selectList("courseNamespace.findAllByName",name);
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			MybatisUtil.closeSqlSession();
		}
	}
	/**
	 * 查询java课程有哪些学生选修
	 * @param name 表示学生的课程
	 */
	public List<Student> findAllByCourseName(String name) throws Exception{
		SqlSession sqlSession = null;
		try{
			sqlSession = MybatisUtil.getSqlSession();
			return sqlSession.selectList("studentNamespace.findAllByCourseName",name);
		}catch(Exception e){
			e.printStackTrace();
			throw e;
		}finally{
			MybatisUtil.closeSqlSession();
		}
	}
	
	public static void main(String[] args) throws Exception{
		StudentCourseDao dao = new StudentCourseDao();
		List<Course> courseList = dao.findAllByName("哈哈");
		System.out.print("哈哈选学了" + courseList.size()+"个课程,分别是:");
		for(Course c : courseList){
			System.out.print(c.getName()+" ");
		}
		System.out.println("\n---------------------");
		List<Student> studentList = dao.findAllByCourseName("android");
		System.out.println("选修了android课程的学生有"+studentList.size()+"个,分别是:");
		for(Student s : studentList){
			System.out.print(s.getName()+" ");
		}
	}
}

6 Mybatis逆向工程

使用官方网站的mapper自动生成工具mybatis-generator-core-1.3.2来生成po类和mapper映射文件。

作用:mybatis官方提供逆向工程,可以使用它通过数据库中的表来自动生成Mapper接口和映射文件(单表增删改查)和Po类.

导入的jar包有:

6.1 第一步:mapper生成配置文件:

在generatorConfig.xml中配置mapper生成的详细信息,注意改下几点:
1、 添加要生成的数据库表
2、 po文件所在包路径
3、 mapper文件所在包路径

配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
	<context id="caigouTables" targetRuntime="MyBatis3">
		<commentGenerator>
			<!-- 是否去除自动生成的注释 true:是 : false:-->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>
		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
		 <jdbcConnection driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"   password="root">
		</jdbcConnection> 
		<!--
		<jdbcConnection driverClass="oracle.jdbc.OracleDriver"
			connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl" 
			userId="scott"
			password="tiger">
		</jdbcConnection>
		-->
		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer true,把JDBC DECIMAL 和 
			NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成PO类的位置 -->
		<javaModelGenerator targetPackage="cn.atcast.pojo"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
        <!-- targetPackage:mapper映射文件生成的位置 -->
		<sqlMapGenerator targetPackage="cn.atcast.mapper" 
			targetProject=".\src">
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper接口的生成位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="cn.atcast.mapper" 
			targetProject=".\src">
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		
		<!-- 指定表 -->
		<table schema="" tableName="user" /> 
		<table schema="" tableName="orders" /> 

	</context>
</generatorConfiguration>

6.2 第二步:使用java类生成mapper文件:

GeneratorSqlmap.java

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlmap {
	public void generator() throws Exception{
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("generatorConfig-base.xml"); 
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
				callback, warnings);
		myBatisGenerator.generate(null);
	} 
	public static void main(String[] args) throws Exception {
		try {
			GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
			generatorSqlmap.generator();
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}

6.3 第三步:拷贝生成的mapper文件到工程中指定目录中

6.3.1 Mapper.xml
Mapper.xml的文件拷贝至mapper目录内
6.3.2 Mapper.java
Mapper.java的文件拷贝至mapper 目录内

注意:mapper xml文件和mapper.java文件在一个目录内且文件名相同。

6.4 第四步Mapper接口测试

学会使用mapper自动生成的增、删、改、查方法。
cn.atcast.mapper/UserMapper.java

package cn.atcast.test;

import java.io.InputStream;
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.Before;
import org.junit.Test;
import cn.atcast.mapper.UserMapper;
import cn.atcast.pojo.User;

public class UserMapperTest {
	private SqlSessionFactory factory;
	// 作用:在测试方法前执行这个方法
	@Before
	public void setUp() throws Exception {
		String resource = "SqlMapConfig.xml";
		// 通过流将核心配置文件读取进来
		InputStream inputStream = Resources.getResourceAsStream(resource);
		// 通过核心配置文件输入流来创建会话工厂
	factory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	@Test
	public void testFindUserById() throws Exception {
		SqlSession openSession = factory.openSession();
	UserMapper mapper = openSession.getMapper(UserMapper.class);
		User user=mapper.selectByPrimaryKey(1);
		System.out.println(user);
		 
	}
}

6.5 逆向工程注意事项

6.5.1 Mapper文件内容不覆盖而是追加
XXXMapper.xml文件已经存在时,如果进行重新生成则mapper.xml文件内容不被覆盖而是进行内容追加,结果导致mybatis解析失败。
解决方法:删除原来已经生成的mapper xml文件再进行生成。
Mybatis自动生成的po及mapper.java文件不是内容而是直接覆盖没有此问题。

7 Hibernate与Mybatis对比

首先简单介绍下两者的概念
Hibernate :Hibernate 是当前最流行的ORM框架,对数据库结构提供了较为完整的封装。
Mybatis:Mybatis同样也是非常流行的ORM框架,主要着力点在于POJO 与SQL之间的映射关系。
其次具体从几个方面说一下两者的区别:
1.两者最大的区别
针对简单逻辑,Hibernate和MyBatis都有相应的代码生成工具,可以生成简单基本的DAO层方法。
针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
2.开发难度对比
Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。
而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。
3.sql书写比较
Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。不过没有自己的日志统计,所以要借助log4j来记录日志。
Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。不过Hibernate具有自己的日志统计。
4.数据库扩展性比较
Mybatis由于所有SQL都是依赖数据库书写的,所以扩展性,迁移性比较差。
Hibernate与数据库具体的关联都在XML中,所以HQL对具体是用什么数据库并不是很关心。
5.缓存机制比较
相同点:Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓存方案,创建适配器来完全覆盖缓存行为。
不同点:Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。
MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
两者比较:因为Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。
而MyBatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常运行带来很大的隐患。
6.总结
Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。
而MyBatis的优势是MyBatis可以进行更为细致的SQL优化,可以减少查询字段,并且容易掌握。
Hibernate的优势是DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值