------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥-------------
本篇博客将会解释Hibernate中的连接查询(各种join)
一,目录
1.内链接
1.1显式内连接(inner join)
1.1迫切显式内连接(inner join fetch)
2.外连接
2.1左外连接(left join)
2.2迫切左外连接(left join fetch)
2.3以及为什么我们在hibernate中尽量避免使用右外连接,具体的解释
二,准备工作
1.数据库脚本的引用,我把测试需要的数据库的脚本给兄弟们传一下
DROP TABLE IF EXISTS `dept`; CREATE TABLE `dept` ( `deptId` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, `location` varchar(255) DEFAULT NULL, PRIMARY KEY (`deptId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `dept` */ insert into `dept`(`deptId`,`name`,`location`) values (1,'xx部','1楼'),(2,'研发部','2楼'),(3,'销售部','3楼'); /*Table structure for table `emp` */ DROP TABLE IF EXISTS `emp`; CREATE TABLE `emp` ( `empId` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, `sal` double DEFAULT NULL, `job` varchar(255) DEFAULT NULL, `deptNo` int(11) DEFAULT NULL, PRIMARY KEY (`empId`), KEY `FK110A81763AD08` (`deptNo`), CONSTRAINT `FK110A81763AD08` FOREIGN KEY (`deptNo`) REFERENCES `dept` (`deptId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `emp` */ insert into `emp`(`empId`,`name`,`sal`,`job`,`deptNo`) values (1,'a',10000,'财务猿1',1),(2,'Ab',5000,'财务猿2',1),(3,'bAa',6000,'财务猿3',1),(4,'aaA',100000,'程序猿1',2),(5,'aB',50000,'程序猿2',2),(6,'AC',60000,'程序猿3',2),(7,'AD',5000000,'Boss',NULL);
2.实体类的创建:值得注意的是toString()需要做手脚,否者就无限递归了
2.1Dept(部门的实体类)
package cn.dawn.day05.entity; import java.util.HashSet; import java.util.Set; /** * Created by Dawn on 2018/6/4. */ public class Dept { private Integer deptId; private String name; private String location; /*一个部门对应多个员工*/ private Set<Emp> emps=new HashSet<Emp>(); public Integer getDeptId() { return deptId; } public void setDeptId(Integer deptId) { this.deptId = deptId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public Set<Emp> getEmps() { return emps; } public void setEmps(Set<Emp> emps) { this.emps = emps; } @Override public String toString() { return "Dept{" + "deptId=" + deptId + ", name='" + name + '\'' + ", location='" + location + '\'' + '}'; } }
2.2Emp(员工实体类)
package cn.dawn.day05.entity; /** * Created by Dawn on 2018/6/4. */ public class Emp { private Integer empId; private String name; private String job; private Double sal; //多个员工属于一个部门 private Dept dept; public Integer getEmpId() { return empId; } public void setEmpId(Integer empId) { this.empId = empId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public Double getSal() { return sal; } public void setSal(Double sal) { this.sal = sal; } public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; } @Override public String toString() { return "Emp{" + "empId=" + empId + ", name='" + name + '\'' + ", job='" + job + '\'' + ", sal=" + sal + ", dept=" + dept + '}'; } }
3.hbm.xml小配置的配置
3.1Dept.hbm.xml的配置
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.dawn.day05.entity"> <!--如果上面指定package的话,class的name就不必写全类名--> <!--lazy:是否懒加载(延迟加载) 默认值是true,延迟加载--> <!--<class name="Teacher">--> <!--直接加载--> <class name="Dept" lazy="false"> <!--主键--> <id name="deptId" column="deptId"> <!--主键生成策咯 assigned程序员自己创建--> <!--identity是mysql里的自增,一会做增加操作不必再给主键赋值--> <!--increment是先查最大的主键列,在下一条给主键加一--> <!--sequence是oracle的主键生成策咯,他一会需要指定序列名字<param name="sequence">序列名</param>--> <generator class="assigned"></generator> </id> <property name="name"/> <property name="location"/> <!--table指的是中间表,承载关联关系的表--> <set name="emps" cascade="all" inverse="true"> <key> <!--本类表的id--> <column name="deptNo"/> </key> <!--另外与他有多对多关联的实体类--> <one-to-many class="Emp"/> </set> </class> </hibernate-mapping>
3.2Emp.hbm.xml小配置的配置
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.dawn.day05.entity"> <!--如果上面指定package的话,class的name就不必写全类名--> <!--lazy:是否懒加载(延迟加载) 默认值是true,延迟加载--> <!--<class name="Teacher">--> <!--直接加载--> <class name="Emp" lazy="false"> <!--主键--> <id name="empId" column="empId"> <!--主键生成策咯 assigned程序员自己创建--> <!--identity是mysql里的自增,一会做增加操作不必再给主键赋值--> <!--increment是先查最大的主键列,在下一条给主键加一--> <!--sequence是oracle的主键生成策咯,他一会需要指定序列名字<param name="sequence">序列名</param>--> <generator class="assigned"></generator> </id> <property name="name"/> <property name="sal"/> <property name="job"/> <!--table指的是中间表,承载关联关系的表--> <!--另外与他有多对多关联的实体类--> <many-to-one name="dept" column="deptNo" class="Dept"/> </class> </hibernate-mapping>
4.hibernate.cfg.xml这个Hibernate核心配置的配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!--jdbc连接四要素--> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///y2167</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password"></property> <!--在控制台上展示sql--> <property name="show_sql">true</property> <!--格式化sql--> <!--<property name="format_sql">true</property>--> <!--====================================================================--> <!--ddl操作生成策咯,每次运行都在原有表上修改,没有的话就创建表--> <!-- <property name="hbm2ddl.auto"></property> key - hbm2ddl.auto:自动生成表结构策略 value - update(使用最多):当数据库不存在表时,hibernate启动后会自动生成表结构。 当数据库表存在时,如果一样,则只会写入数据,不会改变表结构。 当数据库表存在时,如果不一样,则会修改表结构,原有的表结构不会改变。 create(很少):无论表结构是否存在,hibernate启动后都会重新生成表结构。(造成之前的数据丢失) create-drop(极少):无论表结构是否存在,hibernate启动都会重新生成表结构。并且hibernate关闭后,表结构会被删除。来无影去无踪。 validate(很少):不会创建表结构,不会修改表结构。校验与数据库中的表结构是否一样,如果不一样则报异常。 --> <property name="hbm2ddl.auto">update</property> <!--使用getCurrentSession()需要配置此标签--> <property name="current_session_context_class">thread</property> <!--与小配置文件映射--> <mapping resource="cn/dawn/day05/dao/Dept.hbm.xml"></mapping> <mapping resource="cn/dawn/day05/dao/Emp.hbm.xml"></mapping> </session-factory> </hibernate-configuration>
三,显式内连接(inner join)
@Test /*内连接*/ /*返回的是Object[]数组,只有6条数据*/ public void t1innerjoin(){ String hql="from Emp e inner join e.dept"; List<Object[]> list = session.createQuery(hql).list(); for(Object[] o:list){ System.out.println(o[0]);//Emp对象 System.out.println(o[1]);//Dept对象 } /* Hibernate: alter table Emp add constraint FK42q0wt6tw3e1tcygsbj6pexh foreign key (deptNo) references Dept (deptId) Hibernate: select emp0_.empId as empId1_1_0_, dept1_.deptId as deptId1_0_1_, emp0_.name as name2_1_0_, emp0_.sal as sal3_1_0_, emp0_.job as job4_1_0_, emp0_.deptNo as deptNo5_1_0_, dept1_.name as name2_0_1_, dept1_.location as location3_0_1_ from Emp emp0_ inner join Dept dept1_ on emp0_.deptNo=dept1_.deptId Emp{empId=1, name='a', job='财务猿1', sal=10000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Dept{deptId=1, name='xx部', location='1楼'} Emp{empId=2, name='Ab', job='财务猿2', sal=5000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Dept{deptId=1, name='xx部', location='1楼'} Emp{empId=3, name='bAa', job='财务猿3', sal=6000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Dept{deptId=1, name='xx部', location='1楼'} Emp{empId=4, name='aaA', job='程序猿1', sal=100000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Dept{deptId=2, name='研发部', location='2楼'} Emp{empId=5, name='aB', job='程序猿2', sal=50000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Dept{deptId=2, name='研发部', location='2楼'} Emp{empId=6, name='AC', job='程序猿3', sal=60000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Dept{deptId=2, name='研发部', location='2楼'} * */ }
四,迫切显式内连接(inner join fetch)
@Test /*迫切内连接*/ /*返回的是Object,只有6条数据*/ public void t2innerjoinFetch(){ String hql="from Emp e inner join fetch e.dept"; List<Emp> list = session.createQuery(hql).list(); for(Emp e:list){ System.out.println(e);//Emp对象 } /* Hibernate: select emp0_.empId as empId1_1_0_, dept1_.deptId as deptId1_0_1_, emp0_.name as name2_1_0_, emp0_.sal as sal3_1_0_, emp0_.job as job4_1_0_, emp0_.deptNo as deptNo5_1_0_, dept1_.name as name2_0_1_, dept1_.location as location3_0_1_ from Emp emp0_ inner join Dept dept1_ on emp0_.deptNo=dept1_.deptId Emp{empId=1, name='a', job='财务猿1', sal=10000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=2, name='Ab', job='财务猿2', sal=5000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=3, name='bAa', job='财务猿3', sal=6000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=4, name='aaA', job='程序猿1', sal=100000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=5, name='aB', job='程序猿2', sal=50000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=6, name='AC', job='程序猿3', sal=60000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} * */ }
五,左外连接(left join)
@Test /*左外连接*/ /*返回的是Object[]数组,有7条数据,多出来一条,以Emp表为准*/ public void t3leftjoin(){ String hql="from Emp e left join e.dept"; List<Object[]> list = session.createQuery(hql).list(); for(Object[] o:list){ System.out.println(o[0]);//Emp对象 System.out.println(o[1]);//Dept对象 } /* Hibernate: select emp0_.empId as empId1_1_0_, dept1_.deptId as deptId1_0_1_, emp0_.name as name2_1_0_, emp0_.sal as sal3_1_0_, emp0_.job as job4_1_0_, emp0_.deptNo as deptNo5_1_0_, dept1_.name as name2_0_1_, dept1_.location as location3_0_1_ from Emp emp0_ left outer join Dept dept1_ on emp0_.deptNo=dept1_.deptId Emp{empId=1, name='a', job='财务猿1', sal=10000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Dept{deptId=1, name='xx部', location='1楼'} Emp{empId=2, name='Ab', job='财务猿2', sal=5000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Dept{deptId=1, name='xx部', location='1楼'} Emp{empId=3, name='bAa', job='财务猿3', sal=6000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Dept{deptId=1, name='xx部', location='1楼'} Emp{empId=4, name='aaA', job='程序猿1', sal=100000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Dept{deptId=2, name='研发部', location='2楼'} Emp{empId=5, name='aB', job='程序猿2', sal=50000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Dept{deptId=2, name='研发部', location='2楼'} Emp{empId=6, name='AC', job='程序猿3', sal=60000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Dept{deptId=2, name='研发部', location='2楼'} Emp{empId=7, name='AD', job='Boss', sal=5000000.0, dept=null} null * */ }
六,迫切左外连接(left join fetch)
@Test /*迫切左外连接*/ /*返回的是Object,有7条数据*/ public void t4leftjoinFetch(){ String hql="from Emp e left join fetch e.dept"; List<Emp> list = session.createQuery(hql).list(); for(Emp e:list){ System.out.println(e);//Emp对象 } /* Hibernate: select emp0_.empId as empId1_1_0_, dept1_.deptId as deptId1_0_1_, emp0_.name as name2_1_0_, emp0_.sal as sal3_1_0_, emp0_.job as job4_1_0_, emp0_.deptNo as deptNo5_1_0_, dept1_.name as name2_0_1_, dept1_.location as location3_0_1_ from Emp emp0_ left outer join Dept dept1_ on emp0_.deptNo=dept1_.deptId Emp{empId=1, name='a', job='财务猿1', sal=10000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=2, name='Ab', job='财务猿2', sal=5000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=3, name='bAa', job='财务猿3', sal=6000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=4, name='aaA', job='程序猿1', sal=100000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=5, name='aB', job='程序猿2', sal=50000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=6, name='AC', job='程序猿3', sal=60000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=7, name='AD', job='Boss', sal=5000000.0, dept=null} * */ }
七,以及为什么我们在hibernate中尽量避免使用右外连接,具体的解释
以右表为准,返回的左表对象,如果右表有一个null值做关联,那么左表的对象就是null,如果把这个对象直接访问其属性,就会引发空指针异常
所以,我们一般不会使用右外连接
@Test /*迫切右外连接*/ /*返回的是Object,*/ /*hibernate中一般不使用他,不是不能用,而是特别容易出现空指针的异常*/ /*注意观察最后一行查出来的数据,如果我用访问他的属性,这就很难受了*/ /*public void t5rightjoinFetch(){ String hql="from Emp e right join fetch e.dept"; List<Emp> list = session.createQuery(hql).list(); for(Emp e:list){ System.out.println(e);//Emp对象 } *//* Hibernate: select emp0_.empId as empId1_1_0_, dept1_.deptId as deptId1_0_1_, emp0_.name as name2_1_0_, emp0_.sal as sal3_1_0_, emp0_.job as job4_1_0_, emp0_.deptNo as deptNo5_1_0_, dept1_.name as name2_0_1_, dept1_.location as location3_0_1_ from Emp emp0_ right outer join Dept dept1_ on emp0_.deptNo=dept1_.deptId Emp{empId=1, name='a', job='财务猿1', sal=10000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=2, name='Ab', job='财务猿2', sal=5000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=3, name='bAa', job='财务猿3', sal=6000.0, dept=Dept{deptId=1, name='xx部', location='1楼'}} Emp{empId=4, name='aaA', job='程序猿1', sal=100000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=5, name='aB', job='程序猿2', sal=50000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} Emp{empId=6, name='AC', job='程序猿3', sal=60000.0, dept=Dept{deptId=2, name='研发部', location='2楼'}} null * *//* }*/