Hibernate Criteria查詢之多表連線分頁-1
阿新 • • 發佈:2019-02-04
最近嘗試用Hibernate的Criteria查詢實現多表連線下的分頁,發現一些Hibernate的奇怪問題:多表連線後的分頁求總條數始終報錯,檢查生成的sql也不正確。研究許久找到問題所在,特貼如下:
Mysql指令碼如下:
學生 測試資料,請勿見笑:)
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Version : 50018 Source Host : localhost:3306 Source Database : house Target Server Type : MYSQL Target Server Version : 50018 File Encoding : 65001 Date: 2011-08-10 13:38:12 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `district` -- ---------------------------- DROP TABLE IF EXISTS `district`; CREATE TABLE `district` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(50) NOT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of district -- ---------------------------- INSERT INTO `district` VALUES ('1', '青羊區'); INSERT INTO `district` VALUES ('2', '金牛區'); INSERT INTO `district` VALUES ('3', '撫琴小區'); INSERT INTO `district` VALUES ('4', '朝陽區'); INSERT INTO `district` VALUES ('5', '陽光小區'); INSERT INTO `district` VALUES ('6', '名揚小區'); -- ---------------------------- -- Table structure for `house` -- ---------------------------- DROP TABLE IF EXISTS `house`; CREATE TABLE `house` ( `ID` int(11) NOT NULL auto_increment, `user_id` int(11) NOT NULL, `type_id` int(11) NOT NULL, `title` varchar(50) NOT NULL, `description` varchar(2000) NOT NULL, `price` int(11) NOT NULL, `pubdate` date NOT NULL, `floorage` int(11) NOT NULL, `contact` varchar(255) NOT NULL, `street_id` int(11) NOT NULL, PRIMARY KEY (`ID`), KEY `fk_user_house` (`user_id`), KEY `fk_type_house` (`type_id`), KEY `fk_street_house` (`street_id`), CONSTRAINT `fk_street_house` FOREIGN KEY (`street_id`) REFERENCES `street` (`ID`), CONSTRAINT `fk_type_house` FOREIGN KEY (`type_id`) REFERENCES `type` (`ID`), CONSTRAINT `fk_user_house` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of house -- ---------------------------- INSERT INTO `house` VALUES ('1', '1', '1', '第一個房子', '很好,向陽,方便舒適', '1234', '2011-07-26', '123', '1354135423', '1'); INSERT INTO `house` VALUES ('2', '2', '2', '第二個房子', '簡單傢俱,寬敞明亮', '4543', '2011-07-04', '343', '1435465656', '3'); INSERT INTO `house` VALUES ('3', '1', '1', '第三個房子', '乾淨寬敞明亮', '123', '2011-07-13', '123', '1342533333', '3'); INSERT INTO `house` VALUES ('4', '3', '4', '第四個房子', '和低速復甦阿發塑封股', '214', '2011-07-04', '3444', '323243423442', '5'); INSERT INTO `house` VALUES ('5', '3', '3', '第五間房', '是不是哦馬哈好似奧揮灑出', '12', '2011-07-03', '878', '13541234567', '3'); INSERT INTO `house` VALUES ('6', '4', '2', '第六間房', '非hiusa公司udgfbsdugf 歲噶', '344', '2011-07-12', '546', '1423343243', '3'); INSERT INTO `house` VALUES ('8', '1', '4', 'wrwegrhtrhgh', 'dgv hyhhhhhhhhhhhhh', '44', '2011-07-11', '676', '154354545455', '1'); INSERT INTO `house` VALUES ('9', '1', '2', 'gfdgggggggggggggg', 'afdgdsgdgdf', '2342', '2011-06-27', '12423', '1535436546', '1'); INSERT INTO `house` VALUES ('10', '1', '2', 'dfegggg', 'fdfdgfd', '21423', '2011-07-10', '1243', '243254354', '1'); INSERT INTO `house` VALUES ('11', '2', '2', 'rrrrr', 'vdfgrdtg', '2423', '2011-07-11', '232', '13423432435', '2'); -- ---------------------------- -- Table structure for `street` -- ---------------------------- DROP TABLE IF EXISTS `street`; CREATE TABLE `street` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(50) NOT NULL, `district_id` int(11) NOT NULL, PRIMARY KEY (`ID`), KEY `fk_street_district_id` (`district_id`), CONSTRAINT `fk_street_district_id` FOREIGN KEY (`district_id`) REFERENCES `district` (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of street -- ---------------------------- INSERT INTO `street` VALUES ('1', '東南路', '1'); INSERT INTO `street` VALUES ('2', '知春路', '1'); INSERT INTO `street` VALUES ('3', '中關村大街', '2'); INSERT INTO `street` VALUES ('4', '學院路', '3'); INSERT INTO `street` VALUES ('5', '朝陽路', '3'); INSERT INTO `street` VALUES ('6', '成蔭路', '4'); INSERT INTO `street` VALUES ('7', '哈哈路', '3'); -- ---------------------------- -- Table structure for `type` -- ---------------------------- DROP TABLE IF EXISTS `type`; CREATE TABLE `type` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(10) NOT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of type -- ---------------------------- INSERT INTO `type` VALUES ('1', '一室一廳'); INSERT INTO `type` VALUES ('2', '兩室一廳'); INSERT INTO `type` VALUES ('3', '三室一廳'); INSERT INTO `type` VALUES ('4', '四室一廳'); INSERT INTO `type` VALUES ('5', '兩室兩廳'); -- ---------------------------- -- Table structure for `users` -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, `name` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, `telephone` varchar(15) NOT NULL, `userName` varchar(50) NOT NULL, `idAdmin` varchar(5) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of users -- ---------------------------- INSERT INTO `users` VALUES ('1', 'admin', '12345', '13541305274', 'Admin', '1'); INSERT INTO `users` VALUES ('2', 'zhang', '12345', '12345678444', '張小花', '2'); INSERT INTO `users` VALUES ('3', 'li', '12345', '12234242354', 'zhngxaida', '1'); INSERT INTO `users` VALUES ('4', 'luo', 'luo', '143435y4385', 'hsafgdsavds', '1'); INSERT INTO `users` VALUES ('5', 'huang', 'huang', '14235435465', 'hdusigfsu', '1');
現有實體類如下:
區實體類:
package org.accp.mhouse.entities; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; /** * District entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "district", catalog = "house") public class District implements java.io.Serializable { // Fields private Integer id; private String name; private Set<Street> streets = new HashSet<Street>(0); // Constructors /** default constructor */ public District() { } /** minimal constructor */ public District(String name) { this.name = name; } /** full constructor */ public District(String name, Set<Street> streets) { this.name = name; this.streets = streets; } // Property accessors @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "ID", unique = true, nullable = false) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @Column(name = "name", nullable = false, length = 50) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "district") public Set<Street> getStreets() { return this.streets; } public void setStreets(Set<Street> streets) { this.streets = streets; } }
街道實體類:
package org.accp.mhouse.entities; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; /** * Street entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "street", catalog = "house") public class Street implements java.io.Serializable { // Fields private Integer id; private District district; private String name; private Set<House> houses = new HashSet<House>(0); // Constructors /** default constructor */ public Street() { } /** minimal constructor */ public Street(District district, String name) { this.district = district; this.name = name; } /** full constructor */ public Street(District district, String name, Set<House> houses) { this.district = district; this.name = name; this.houses = houses; } // Property accessors @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "ID", unique = true, nullable = false) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "district_id", nullable = false) public District getDistrict() { return this.district; } public void setDistrict(District district) { this.district = district; } @Column(name = "name", nullable = false, length = 50) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "street") public Set<House> getHouses() { return this.houses; } public void setHouses(Set<House> houses) { this.houses = houses; } }
房屋型別實體類:
package org.accp.mhouse.entities;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* Type entity. @author MyEclipse Persistence Tools
*/
@Entity
@Table(name = "type", catalog = "house")
public class Type implements java.io.Serializable {
// Fields
private Integer id;
private String name;
private Set<House> houses = new HashSet<House>(0);
// Constructors
/** default constructor */
public Type() {
}
/** minimal constructor */
public Type(String name) {
this.name = name;
}
/** full constructor */
public Type(String name, Set<House> houses) {
this.name = name;
this.houses = houses;
}
// Property accessors
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "name", nullable = false, length = 10)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "type")
public Set<House> getHouses() {
return this.houses;
}
public void setHouses(Set<House> houses) {
this.houses = houses;
}
}
使用者實體類:
package org.accp.mhouse.entities;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* Users entity. @author MyEclipse Persistence Tools
*/
@Entity
@Table(name = "users", catalog = "house")
public class Users implements java.io.Serializable {
// Fields
private Integer id;
private String name;
private String password;
private String telephone;
private String userName;
private String idAdmin;
private Set<House> houses = new HashSet<House>(0);
// Constructors
/** default constructor */
public Users() {
}
/** minimal constructor */
public Users(String name, String password, String telephone,
String userName, String idAdmin) {
this.name = name;
this.password = password;
this.telephone = telephone;
this.userName = userName;
this.idAdmin = idAdmin;
}
/** full constructor */
public Users(String name, String password, String telephone,
String userName, String idAdmin, Set<House> houses) {
this.name = name;
this.password = password;
this.telephone = telephone;
this.userName = userName;
this.idAdmin = idAdmin;
this.houses = houses;
}
// Property accessors
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "name", nullable = false, length = 50)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "password", nullable = false, length = 50)
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name = "telephone", nullable = false, length = 15)
public String getTelephone() {
return this.telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
@Column(name = "userName", nullable = false, length = 50)
public String getUserName() {
return this.userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Column(name = "idAdmin", nullable = false, length = 5)
public String getIdAdmin() {
return this.idAdmin;
}
public void setIdAdmin(String idAdmin) {
this.idAdmin = idAdmin;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "users")
public Set<House> getHouses() {
return this.houses;
}
public void setHouses(Set<House> houses) {
this.houses = houses;
}
}
房屋實體類:
package org.accp.mhouse.entities;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* House entity. @author MyEclipse Persistence Tools
*/
@Entity
@Table(name = "house", catalog = "house")
public class House implements java.io.Serializable {
// Fields
private Integer id;
private Users users;//many to one
private Type type;//many to one
private Street street;//many to one
private String title;
private String description;
private Integer price;
private Date pubdate;
private Integer floorage;
private String contact;
// Constructors
/** default constructor */
public House() {
}
/** full constructor */
public House(Users users, Type type, Street street, String title,
String description, Integer price, Date pubdate, Integer floorage,
String contact) {
this.users = users;
this.type = type;
this.street = street;
this.title = title;
this.description = description;
this.price = price;
this.pubdate = pubdate;
this.floorage = floorage;
this.contact = contact;
}
// Property accessors
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
public Users getUsers() {
return this.users;
}
public void setUsers(Users users) {
this.users = users;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "type_id", nullable = false)
public Type getType() {
return this.type;
}
public void setType(Type type) {
this.type = type;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "street_id", nullable = false)
public Street getStreet() {
return this.street;
}
public void setStreet(Street street) {
this.street = street;
}
@Column(name = "title", nullable = false, length = 50)
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
@Column(name = "description", nullable = false, length = 2000)
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
@Column(name = "price", nullable = false)
public Integer getPrice() {
return this.price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Temporal(TemporalType.DATE)
@Column(name = "pubdate", nullable = false, length = 10)
public Date getPubdate() {
return this.pubdate;
}
public void setPubdate(Date pubdate) {
this.pubdate = pubdate;
}
@Column(name = "floorage", nullable = false)
public Integer getFloorage() {
return this.floorage;
}
public void setFloorage(Integer floorage) {
this.floorage = floorage;
}
@Column(name = "contact", nullable = false)
public String getContact() {
return this.contact;
}
public void setContact(String contact) {
this.contact = contact;
}
}
查詢示範1,查詢區為“xxx”區的所有房屋資訊,並抓取查詢房屋物件關聯的街道物件,街道物件關聯的區物件
首先查詢總條數,通常思維習慣如下:
//求總條數
session
.createCriteria(House.class)
.setFetchMode("street", org.hibernate.FetchMode.JOIN)
.setFetchMode("street.district",org.hibernate.FetchMode.JOIN)
.createAlias("street.district", "d")
.add(Restrictions.eq("d.name", "青羊區"))
.setProjection(Projections.rowCount())
.uniqueResult();
很遺憾,查詢結果大失所望:
控制異常如下:
Hibernate:
select
count(*) as y0_
from
house.house this_
where
d1_.name=?
Exception in thread "main" org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.loader.Loader.doList(Loader.java:2220)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2108)
at org.hibernate.loader.Loader.list(Loader.java:2103)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:94)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1570)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:305)
at org.accp.mhouse.dao.MainModule.main(MainModule.java:164)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'd1_.name' in 'where clause'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1030)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1885)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:187)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1791)
at org.hibernate.loader.Loader.doQuery(Loader.java:674)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.doList(Loader.java:2217)
... 7 more
但是以上語句如果是查詢結果那是絕對正確的,如下:
//結果
session
.createCriteria(House.class)
.setFetchMode("street", org.hibernate.FetchMode.JOIN)
.setFetchMode("street.district",org.hibernate.FetchMode.JOIN)
.createAlias("street.district", "d")
.add(Restrictions.eq("d.name", "青羊區"))
.list();
控制檯輸出如下:
Hibernate:
select
this_.ID as ID2_2_,
this_.contact as contact2_2_,
this_.description as descript3_2_2_,
this_.floorage as floorage2_2_,
this_.price as price2_2_,
this_.pubdate as pubdate2_2_,
this_.street_id as street8_2_2_,
this_.title as title2_2_,
this_.type_id as type9_2_2_,
this_.user_id as user10_2_2_,
street3_.ID as ID1_0_,
street3_.district_id as district3_1_0_,
street3_.name as name1_0_,
d1_.ID as ID4_1_,
d1_.name as name4_1_
from
house.house this_
inner join
house.street street3_
on this_.street_id=street3_.ID
inner join
house.district d1_
on street3_.district_id=d1_.ID
where
d1_.name=?
就是上面那個求總條數的問題困擾我許久,網路搜尋無果,幾經查詢摸索,正確如下:
求總條數:
//求總條數
session
.createCriteria(House.class)
.createAlias("street","st",CriteriaSpecification.INNER_JOIN)
.createAlias("st.district","d",CriteriaSpecification.INNER_JOIN)
.createAlias("users", "u", CriteriaSpecification.INNER_JOIN)
.createAlias("type","t",CriteriaSpecification.INNER_JOIN)
.add(Restrictions.eq("d.name", "青羊區"))
.setProjection(Projections.rowCount())
.uniqueResult();
Hibernate API文件對createAlias()方法的解釋:
createAlias
-
- Parameters:
associationPath
- A dot-seperated property pathalias
- The alias to assign to the joined association (for later reference).joinType
- The type of join to use.- Returns:
- this (for method chaining)
- Throws:
很明顯上面的查詢更加複雜,房屋關聯街道,街道關聯區,房屋關聯型別,房屋關聯使用者,篩選條件是區是“青羊區”,我們看查詢結果:
Hibernate:
select
count(*) as y0_
from
house.house this_
inner join
house.street st1_
on this_.street_id=st1_.ID
inner join
house.district d2_
on st1_.district_id=d2_.ID
inner join
house.type t4_
on this_.type_id=t4_.ID
inner join
house.users u3_
on this_.user_id=u3_.id
where
d2_.name=?
OK,終於搞定這個棘手的問題。