Статьи

Hibernate: используйте псевдоним соединительной таблицы на sqlRestriction

При использовании hibernate в случае сложных запросов возникает необходимость использовать sql.
Поэтому на помощь приходят sqlRestrictions. Однако использование sql-ограничений для псевдонима таблицы соединения немного сложно.

Там будет три таблицы:

  • Фирменный стол.
  • Стол сотрудника.
    Каждый сотрудник принадлежит одной компании, поэтому у нас есть отношения многие к одному.
  • Таблица преимуществ.
    У каждой компании есть много преимуществ, поэтому у нас есть отношения один ко многим.

Postgresql будет использоваться для этого примера.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CREATE DATABASE example;
 
CREATE TABLE company (
  company_id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);
 
CREATE TABLE employee (
  employee_id SERIAL PRIMARY KEY,
  first_name TEXT,
  last_name TEXT,
  company_id integer,
  CONSTRAINT fk_company FOREIGN KEY (company_id)
    REFERENCES company (company_id) MATCH SIMPLE
);
 
CREATE TABLE benefit (
  benefit_id SERIAL PRIMARY KEY,
  name TEXT,
  company_id integer,
  CONSTRAINT fk_company FOREIGN KEY (company_id)
  REFERENCES company (company_id) MATCH SIMPLE
);
 
INSERT INTO company (name) VALUES ('TestCompany');
INSERT INTO employee (first_name, last_name, company_id) VALUES ('Emmanouil','Gkatziouras',1);
INSERT INTO benefit (name,company_id) VALUES ('gym',1);

JPA будет использоваться для конфигурации объекта.

Субъект компании.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.gkatzioura.example.entity;
 
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
 
/**
 * Created by gkatziourasemmanouil on 7/12/15.
 */
@Entity
@Table(name = "company")
public class Company {
 
    @Id
    @GeneratedValue
    @Column(name = "company_id")
    private Long Id;
 
    @Column
    String name;
 
    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "company")
    private Set<Benefit> benefits = new HashSet<Benefit>();
 
    public Long getId() {
        return Id;
    }
 
    public void setId(Long id) {
        Id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Set<Benefit> getBenefits() {
        return benefits;
    }
 
    public void setBenefits(Set<Benefit> benefits) {
        this.benefits = benefits;
    }
}

Сотрудник юридического лица.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.gkatzioura.example.entity;
 
import javax.persistence.*;
 
/**
 * Created by gkatziourasemmanouil on 8/2/15.
 */
@Entity
@Table(name = "employee")
public class Employee {
 
    @Id
    @GeneratedValue
    @Column(name = "employee_id")
    private Long id;
 
    @Column(name = "first_name")
    private String firstName;
 
    @Column(name = "last_name")
    private String lastName;
 
    @ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id",referencedColumnName = "company_id")
    private Company company;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public Company getCompany() {
        return company;
    }
 
    public void setCompany(Company company) {
        this.company = company;
    }
 
    public String getLastName() {
        return lastName;
    }
 
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
 
    public String getFirstName() {
        return firstName;
    }
 
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

Выгода субъекта.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.gkatzioura.example.entity;
 
import javax.persistence.*;
 
/**
 * Created by gkatziourasemmanouil on 8/9/15.
 */
 
@Entity
@Table(name = "benefit")
public class Benefit {
 
    @Id
    @GeneratedValue
    @Column(name = "benefit_id")
    private Long id;
 
    @Column(name = "name")
    private String name;
 
    @ManyToOne
    @JoinColumn(name = "company_id")
    private Company company;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public Company getCompany() {
        return company;
    }
 
    public void setCompany(Company company) {
        this.company = company;
    }
}

Конфигурация гибернации

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 
<hibernate-configuration>
 
    <session-factory>
 
        <!-- Database connection settings -->
        <property name="connection.driver_class">org.postgresql.Driver</property>
        <property name="connection.url">jdbc:postgresql://127.0.0.1:5432/example</property>
        <property name="connection.username">postgres</property>
        <property name="connection.password">postgres</property>
 
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
 
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.PostgreSQL9Dialect</property>
 
        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>
 
        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
 
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
 
        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">update</property>
 
        <mapping class="com.gkatzioura.example.entity.Company"/>
        <mapping class="com.gkatzioura.example.entity.Employee"/>
        <mapping class="com.gkatzioura.example.entity.Benefit"/>
 
    </session-factory>
 
</hibernate-configuration>

Основной класс, создающий фабрику сессий Hibernate

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.gkatzioura.example;
 
import com.gkatzioura.example.entity.Employee;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.hibernate.type.StringType;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * Created by gkatziourasemmanouil on 7/12/15.
 */
public class Main {
 
    private static SessionFactory sessionFactory;
 
    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SessionFactory.class);
 
    public static void main(String args[]) {
 
        sessionFactory = buildSessionFactory();
 
        getByCompanyAndBenefit("TestCompany","gym");
 
        sessionFactory.close();
    }
 
    public static SessionFactory buildSessionFactory() {
 
        return new Configuration()
                .configure()
                .buildSessionFactory();
    }
 
     
}

Предположим, что мы хотим получать сотрудников по названию компании и конкретному названию льготы, мы добавим функцию getByCompanyAndBenefit.

Название компании будет отфильтровано по обычному ограничению.

Однако имя выгоды будет отфильтровано sqlRestriction.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
private static List<Employee> getByCompanyAndBenefit(String companyName,String benefitName) {
 
        Session session = sessionFactory.openSession();
 
        Criteria criteria = session.createCriteria(Employee.class,"employee");
 
        Criteria companyCriteria = criteria.createCriteria("company",JoinType.INNER_JOIN);
        companyCriteria.add(Restrictions.eq("name", companyName));
        Criteria benefitsAlias = companyCriteria.createCriteria("benefits",JoinType.LEFT_OUTER_JOIN);
        benefitsAlias.add(Restrictions.sqlRestriction("{alias}.name = ?",benefitName, StringType.INSTANCE));
 
        List<Employee> employees = criteria.list();
 
        for(Employee employee:employees) {
            LOGGER.error("The employee is "+employee.getFirstName());
        }
 
        session.close();
 
        return employees;
    }

При использовании функции createCriteria критерия для отношения {псевдоним} представляет связанный объект новых критериев.

Это невозможно с классом критериев, полученным после использования createAlias.

Последний, но не менее важный файл.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
group 'com.gkatzioura.example'
version '1.0-SNAPSHOT'
 
apply plugin: 'application'
apply plugin: 'java'
apply plugin: 'idea'
 
mainClassName = "com.gkatzioura.example.Main"
 
sourceCompatibility = 1.8
 
repositories {
    mavenLocal()
    mavenCentral()
}
 
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
 
    compile 'org.ancoron.postgresql:org.postgresql.net:9.1.901.jdbc4.1-rc9'
    compile 'org.slf4j:slf4j-api:1.6.6'
    compile 'ch.qos.logback:logback-classic:1.0.13'
    compile 'org.hibernate:hibernate-core:4.3.6.Final'
    compile 'org.hibernate:hibernate-entitymanager:4.3.6.Final'
    compile 'org.hibernate:hibernate-validator:5.1.1.Final'
    compile 'dom4j:dom4j:1.6.1'
    compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
 
    testCompile group: 'junit', name: 'junit', version:'3.8.1'
 
}