Hibernate ORM – Podstawy

Zapisywanie informacji do bazy danych jest niezwykle istotnym zadaniem niemal każdej bardziej zaawansowanej aplikacji. Istnieje wiele rozwiązań pozwalających nam to osiągnąć nadal najczęściej jednak korzysta się z relacyjnych baz danych oraz języka SQL. Jednym zaś z popularniejszy rozwiązań w przypadku aplikacji internetowych napisanych w Javie jest wykorzystanie frameworka Hibernate.


 
Hibernate ORM jest frameworkiem umożliwiającym mapowanie obiektów Javy na relacyjne tablice w bazie danych. Hibernate działa na podstawie JDBC API pomijając konieczność wielokrotnego powtarzania tego samego kodu, ułatwiając i przyspiesza przy tym większość operacji związanych z współpracą naszej aplikacji z bazą danych.
 
W poniższym instruktażu zostanie pokazane w jaki sposób skonfigurować hibernate z naszym projektem. Jak tworzyć relacje jeden do jednego, jeden do wielu oraz wiele do wielu. Przedstawione zostaną podstawowe zapytania przy wykorzystaniu języka HQL. Klasy konfigurowane będą przy użyciu anotacji.
 
Do stworzenia naszej aplikacji będziemy potrzebowali kilku rzeczy. Są nimi odpowiednie biblioteki które możemy ściągnąć za pomocą Mavena, plik konfiguracyjny hibernate, odpowiednie tabele stworzone w bazie danych, POJO klas odwzorowujących obiekty zapisywane w bazie danych oraz sam kod źródłowy. Przejdźmy więc do pracy
 
Na początek stwórzmy Mavenowy projekt i ściągnijmy potrzebne biblioteki:
 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.binaryalchemist</groupId>
	<artifactId>killer_robot</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>killer_robot Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>

			<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<scope>provided</scope>
			<version>3.1.0</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet.jsp.jstl</groupId>
			<artifactId>javax.servlet.jsp.jstl-api</artifactId>
			<version>1.2.1</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.glassfish.web</groupId>
			<artifactId>javax.servlet.jsp.jstl</artifactId>
			<version>1.2.2</version>
			<scope>compile</scope>
			<exclusions>
				<exclusion>
					<groupId>javax.servlet</groupId>
					<artifactId>servlet-api</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.servlet.jsp</groupId>
					<artifactId>jsp-api</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.servlet.jsp.jstl</groupId>
					<artifactId>jstl-api</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>javax.el</groupId>
			<artifactId>javax.el-api</artifactId>
			<version>3.0.0</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>javax.persistence</artifactId>
			<version>2.1.0</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>javax.transaction</groupId>
			<artifactId>javax.transaction-api</artifactId>
			<version>1.2</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.3.1.Final</version>
			<scope>runtime</scope>
			<exclusions>
				<exclusion>
					<groupId>org.hibernate.javax.persistence</groupId>
					<artifactId>hibernate-jpa-2.1-api</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.jboss.spec.javax.transaction</groupId>
					<artifactId>jboss-transaction-api_1.2_spec</artifactId>
				</exclusion>
				<exclusion>
					<groupId>xml-apis</groupId>
					<artifactId>xml-apis</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.jboss.logging</groupId>
					<artifactId>jboss-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.jboss.logging</groupId>
			<artifactId>jboss-logging</artifactId>
			<version>3.1.4.GA</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>

		
	</dependencies>
	<build>
		<finalName>killer_robot</finalName>
		<plugins>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<configuration>
					<webXml>src\main\webapp\WEB-INF\web.xml</webXml>
				</configuration>

			</plugin>

		</plugins>

	</build>
</project>

Biblioteki związane z hibernatem zaczynają się od org.eclipse.persistence wcześniejsze biblioteki mogą nam się przydać jeśli tworzymy aplikacje webową.
 
Żeby nasza aplikacja mogła łączyć się z bazą danych konieczne jest stworzenie pliku konfiguracyjnego hibernate.cfg.xml w folderze src/main/resources Plik ten może wyglądać następująco:
 

<?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>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/robot</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>

<property name="show_sql">true</property>
<property name="format_sql">true</property>

<mapping class="com.binaryalchemist.killer_robot.KillerRobot"/>

</session-factory>

</hibernate-configuration>

 
Pierwsze parametry odpowiedzialne są za konfiguracje i połączenie z bazą danych. Podajemy tu informacje z jakiej bazy danych korzystamy (w moim wypadku MySQL) adres naszej bazy (localhost:3306/robot) nazwę użytkownika i hasło. Kolejne parametry pozwalają na pokazywanie wyników działania hibernate na konsoli. Na końcu ważna jest linia „” służą ona bowiem do mapowania naszych klas dzięki czego hibernate będzie wiedział którą i jak zamienić klasę na obiekt w bazie danych. Tutaj została mapowana klasa KillerRobot którą stworzymy w kolejnych krokach.
 

Stwórzmy więc tabele reprezentującego naszego robota typu terminator:
 

create table robot
(
robot_id int NOT NULL AUTO_INCREMENT,
name varchar(255),
serial varchar(255),
time datetime,
constraint primary key(robot_id)
)

 
Jeśli znamy SQL nie powinno tu być nić nadzwyczajnego. Kolejna rzecz jaką musimy zrobić to stworzyć klasę Javy odzwierciedlającą ten sam obiekt. Stwórzmy więc następującą klasę KillerRobot.
 

@Entity
@Table(name="robot")
public class KillerRobot{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name = "robot_id", unique = true, nullable = false)
	private long id;
	
	@Column(name = "name", unique = false, nullable = true)
	private String name;
	
	@Column(name = "serial", unique = false, nullable = true)
	private String serial;
	
	@Column(name = "time", unique = false, nullable = true)
	private Date productionTime;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSerial() {
		return serial;
	}
	public void setSerial(String serial) {
		this.serial = serial;
	}
	public Date getProductionTime() {
		return productionTime;
	}
	public void setProductionTime(Date productionTime) {
		this.productionTime = productionTime;
	}

}

Anotacja @Entity mówi nam że jest to obiekt który chcemy mapować na bazę danych. @Table wraz z parametrem name informuję na jaką tabele rzutujemy nasz obiekt. W naszym wypadku jest to niedawno stworzona tabela robot. Każdy robot posiada własny numer identyfikacyjny. Chcemy jednak by numer ten był generowany automatycznie dlatego korzystamy z anotacji @GeneratedValue(strategy=GenerationType.IDENTITY)
GenericGenerator odpowiada za sposób w jaki generowany jest nasz numer identyfkacyjny. Każdą z kolumn możemy indywidualnie skonfigurować aby hibernate wiedział która zmienna odpowiada której wartości w tabeli. Nie jest to jednak konieczne jeśli nasze zmienne mają taką samą nazwę jak zmienne w tabeli.
 
Stwórzmy klasę DBConnection która będzie odpowiedzialna za wczytanie pliku konfiguracyjnego Hibernata oraz utworzenie sesji:
 

public class DBConnection {

	private static SessionFactory factory;

	static {
		Configuration cfg = new Configuration();
		cfg.configure("Hibernate.cfg.xml");

		ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(cfg.getProperties())
				.build();

		factory = cfg.buildSessionFactory(serviceRegistry);
	}

	public static Session getSession() {
		return factory.openSession();
	}

	public void doWork() {}

	public static void close() {
		factory.close();
	}

}

Na koniec wstępu pozostaję nam wreszcie wykorzystać wszystko by zapisać naszego robota w bazie danych:
 

@WebServlet(
name="robotservlet",
urlPatterns={"/robot","/" })
public class KillerRobotServlet extends HttpServlet {
	
	public KillerRobotServlet() {
		super();
	}
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		KillerRobot robi = new KillerRobot();
		robi.setName("Bob");
		robi.setProductionTime(new Date());
		robi.setSerial("AX7N41S");
		
		Session session = DBConnection.getSession();
		session.beginTransaction();
		session.save(robi);
		session.getTransaction().commit();
		DBConnection.close();
		
		request.getRequestDispatcher("/index.jsp").forward(request, response);
	}
	
}

Kod tu jest stosunkowo prosty tworzymy naszego robota zapisujemy go w sesji. Domyślnie operacje w hibernacie wykonywane są przy użyciu transakcji dlatego nie możemy pominąć funkcji session.beginTransaction(); która rozpoczyna transakcje oraz session.getTransaction().commit(); która zatwierdza transakcje. Skoro znamy już podstawy możemy przejść do relacji typu jeden do jednego.
 

Relacja jeden do jedngo Hibernate:

Każdy robot musi być przez kogoś stworzony. Przyjmijmy więc że chcemy informacje na temat wytwórcy robota przechowywać w oddzielnej tabeli i tworzyć tym samym relacje jeden do jednego.
 
Na początek stwórzmy tabele company:
 

create table company
(
robot_id int NOT NULL,
name varchar(255),
adress text,
webpage text,
founded datetime,
constraint primary key(robot_id),
CONSTRAINT company_user_fk FOREIGN KEY(robot_id) REFERENCES robot(robot_id)
)

 
 
Analogicznie jak wcześniej tworzymy klasę Company:
 

@Entity
@Table(name="company")
public class Company{

	@Id
	@Column(name="robot_id", unique=true, nullable=false)
	@GeneratedValue(generator="generator")
	@GenericGenerator(name="generator", strategy="foreign", parameters = @Parameter(name = "property", value = "robot"))
    private long robot_id;
	private String name;
	private String adress;
	private String webpage;
	private Date founded;
	
	@OneToOne(mappedBy="company", cascade=CascadeType.ALL, fetch = FetchType.LAZY)
	private KillerRobot robot;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAdress() {
		return adress;
	}
	public void setAdress(String adress) {
		this.adress = adress;
	}
	public String getWebpage() {
		return webpage;
	}
	public void setWebpage(String webpage) {
		this.webpage = webpage;
	}
	public Date getFounded() {
		return founded;
	}
	public void setFounded(Date founded) {
		this.founded = founded;
	}
	public KillerRobot getRobot() {
		return robot;
	}
	public void setRobot(KillerRobot robot) {
		this.robot = robot;
	}
	public long getRobot_id() {
		return robot_id;
	}
	public void setRobot_id(long robot_id) {
		this.robot_id = robot_id;
	}	
}

Można zauważyć tu dwie różnice. Pierwsza z nich to sposób generowania numeru identyfikacyjnego. Jako że nas numer id będzie pobierany z tabeli robot a nie automatycznie tworzony. Dlatego korzystamy z strategi foreign i jako parametr wskazujemy tabele robot. Korzystamy z @GeneratedValue(generator=”generator”)
@GenericGenerator(name=”generator”, strategy=”foreign”, parameters = @Parameter(name = „property”, value = „robot”))
 

Druga istotna kwestia to anotacja @OneToOne(mappedBy=”company”, cascade=CascadeType.ALL, fetch = FetchType.LAZY)
Anotacja @OneToOne mówi nam ze klasy znajdują się w relacji jeden do jednego i musi ona być poprzedzona obiektem do którego się odnosi w tym wypadku jest to klasa KillerRobot. Parametr mappedBy ma wartość taką samą jak nazwa zmiennej zapisanej w klasie KillerRobot Wartość cascade=CascadeType.ALL mówi nam iż w przypadku aktualizacji usunięcia aktualizujemy także ten obiekt. Fetch mówi nam w jaki sposób pobieramy dane z bazy danych może to być wartość LAZY na temat przechowywanego obiektu są pobierane w momencie gdy są potrzebne lub EAGER gdy dane są pobierane od razu.
 
Anotacja @OneToOne musi być również zapisana w klasie KillerRobot dlatego aktualizujemy tę klasę następująco:
 

@Entity
@Table(name="robot")
public class KillerRobot{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name = "robot_id", unique = true, nullable = false)
	private long id;
	
	@Column(name = "name", unique = false, nullable = true)
	private String name;
	
	@Column(name = "serial", unique = false, nullable = true)
	private String serial;
	
	@Column(name = "time", unique = false, nullable = true)
	private Date productionTime;
	
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name="robot_id")
	private Company company;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSerial() {
		return serial;
	}
	public void setSerial(String serial) {
		this.serial = serial;
	}
	public Date getProductionTime() {
		return productionTime;
	}
	public void setProductionTime(Date productionTime) {
		this.productionTime = productionTime;
	}
	public Company getCompany() {
		return company;
	}
	public void setCompany(Company company) {
		this.company = company;
	}
}

 
Bardzo ważne jest abyśmy w pliku konfiguracyjny, hibernate.cfg.xml nie zapomnieli dodać mapowania nowo powstałej klasy.
 

<mapping class="com.binaryalchemist.killer_robot.Company"/>

Na tym etapie nasza aplikacja prezentuję się następująco:
 

@WebServlet(
name="robotservlet",
urlPatterns={"/robot","/" })
public class KillerRobotServlet extends HttpServlet {
	
	public KillerRobotServlet() {
		super();
	}
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		KillerRobot robi = new KillerRobot();
		robi.setName("Bob");
		robi.setProductionTime(new Date());
		robi.setSerial("AX7N41S");
		
		Company company = new Company();
		company.setName("Skynet");
		company.setAdress("USA");
		company.setWebpage("www.skynet.com");
		company.setFounded(new Date());
		robi.setCompany(company);
		company.setRobot(robi);
		
		Session session = DBConnection.getSession();
		session.beginTransaction();
		session.save(robi);
		session.getTransaction().commit();
		DBConnection.close();
		
		request.getRequestDispatcher("/index.jsp").forward(request, response);
	}
}

Kod nie stał się dużo cięższy pamiętajmy jednak aby obydwa obiekty przypisać do siebie poprzez robi.setCompany(company); company.setRobot(robi); Dalej zajmiemy się relacją jeden do wielu.
 

Relacja jeden do wielu hibernate:

Przyjmijmy że każdy z naszych zabójczych robotów może posiadać od jednej do więcej broni. Stwórzmy więc ponownie następującą tabele i klasę:
 

create table weapon
(
weapon_id int NOT NULL AUTO_INCREMENT,
robot_id int NOT NULL,
name text,
firepower int,
type text,
weight float,
constraint primary key(weapon_id),
CONSTRAINT weapon_robot_fk FOREIGN KEY(robot_id) REFERENCES robot(robot_id)
)

 

@Entity
@Table(name="weapon")
public class Weapon {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name = "weapon_id", unique = true, nullable = false)
	private long weapon_id;
    private String name;
	private int firepower;
    private String type;
    private float weight;
    
    
    @ManyToOne
    @JoinColumn(name = "robot_id")
    private KillerRobot robot;
    
	public long getWeapon_id() {
		return weapon_id;
	}
	public void setWeapon_id(long weapon_id) {
		this.weapon_id = weapon_id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getFirepower() {
		return firepower;
	}
	public void setFirepower(int firepower) {
		this.firepower = firepower;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public float getWeight() {
		return weight;
	}
	public void setWeight(float weight) {
		this.weight = weight;
	}
	public KillerRobot getRobot() {
		return robot;
	}
	public void setRobot(KillerRobot robot) {
		this.robot = robot;
	}
}

 
Doszła nam tu właściwie jedna nowa anotacja @ManyToOne w której wskazujemy według jakiego parametru ma być łączona tabela.
 
Następnie aktualizujemy klasę robot o nową relacje:
 

@Entity
@Table(name="robot")
public class KillerRobot{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name = "robot_id", unique = true, nullable = false)
	private long id;
	
	@Column(name = "name", unique = false, nullable = true)
	private String name;
	
	@Column(name = "serial", unique = false, nullable = true)
	private String serial;
	
	@Column(name = "time", unique = false, nullable = true)
	private Date productionTime;
	
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name="robot_id")
	private Company company;
	
	@OneToMany(mappedBy="robot")
	private Set<Weapon> weapons;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSerial() {
		return serial;
	}
	public void setSerial(String serial) {
		this.serial = serial;
	}
	public Date getProductionTime() {
		return productionTime;
	}
	public void setProductionTime(Date productionTime) {
		this.productionTime = productionTime;
	}
	public Company getCompany() {
		return company;
	}
	public void setCompany(Company company) {
		this.company = company;
	}
	public Set<Weapon> getWeapons() {
		return weapons;
	}
	public void setWeapons(Set<Weapon> weapons) {
		this.weapons = weapons;
	}
}

Warto zauważyć że jako iż robot może posiadać wiele broni annoacja musi wskazywać kolekcje. Dodajmy mapowanie w pliku konfiguracyjnym hibernata:
 

<mapping class="com.binaryalchemist.killer_robot.Weapon" />

Kod źródłowy rozbudowany o nową funkcjonalność prezentuję się następująco:
 

 
@WebServlet(
name="robotservlet",
urlPatterns={"/robot","/" })
public class KillerRobotServlet extends HttpServlet {
	
	public KillerRobotServlet() {
		super();
	}
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		KillerRobot robi = new KillerRobot();
		robi.setName("Bob");
		robi.setProductionTime(new Date());
		robi.setSerial("AX7N41S");
		
		Company company = new Company();
		company.setName("Skynet");
		company.setAdress("USA");
		company.setWebpage("www.skynet.com");
		company.setFounded(new Date());
		robi.setCompany(company);
		company.setRobot(robi);
		
		Set<Weapon> weapons = new HashSet<Weapon>(); 
		
		Weapon weapon1 = new Weapon();
		weapon1.setName("Laser");
		weapon1.setType("Energy");
		weapon1.setFirepower(36);
		weapon1.setWeight(56.6f);
		weapon1.setRobot(robi);
		Weapon weapon2 = new Weapon();
		weapon2.setName("Gattling Gun");
		weapon2.setType("Projectile");
		weapon1.setFirepower(58);
		weapon2.setWeight(172.6f);
		weapon2.setRobot(robi);
		weapons.add(weapon1);
		weapons.add(weapon2);
		
		robi.setWeapons(weapons);
		
		Session session = DBConnection.getSession();
		session.beginTransaction();
		session.save(robi);
		session.save(weapon1);
		session.save(weapon2);
		session.getTransaction().commit();
		DBConnection.close();
		
		request.getRequestDispatcher("/index.jsp").forward(request, response);
	}
}

Zajmiemy się teraz ostatnią relacją czyli relacją wiele do wielu:
 

Relacja wiele do wielu Hibernate:

Każdy robot posiada różne sensory jak również każdy typ sensoru może być zainstalowany w różnych robotach dlatego spróbujmy ten aspekt odzwierciedlić w hibernacie:
 
Tworzymy tabele oraz klasę:
 

create table sensor
(
sensor_id int NOT NULL AUTO_INCREMENT,
name varchar(255),
type text,
description text,
constraint primary key(sensor_id)
)

 

create table robot_sensor
(
robot_id int NOT NULL,
sensor_id int NOT NULL,
CONSTRAINT PRIMARY KEY(robot_id,sensor_id),
CONSTRAINT FOREIGN KEY(robot_id) REFERENCES robot(robot_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT FOREIGN KEY(sensor_id) REFERENCES sensor(sensor_id) ON UPDATE CASCADE ON DELETE CASCADE
)

 

@Entity
@Table(name="sensor")
public class Sensor {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name = "sensor_id", unique = true, nullable = false)
	private long sensor_id;
    private String name;
	private String type;
    private String description;
    
	@ManyToMany(mappedBy = "sensors")
	private Set<KillerRobot> robots;

	public long getSensor_id() {
		return sensor_id;
	}

	public void setSensor_id(long sensor_id) {
		this.sensor_id = sensor_id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Set<KillerRobot> getRobots() {
		return robots;
	}

	public void setRobots(Set<KillerRobot> robots) {
		this.robots = robots;
	}
}

Dodajemy anotacje @ManyToMany i wskazujemy do której zmiennej w klasie KillerRobot jest ona przypisana. Aktualizujemy ponownie naszego KillerRobot
 

@Entity
@Table(name="robot")
public class KillerRobot{

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name = "robot_id", unique = true, nullable = false)
	private long id;
	
	@Column(name = "name", unique = false, nullable = true)
	private String name;
	
	@Column(name = "serial", unique = false, nullable = true)
	private String serial;
	
	@Column(name = "time", unique = false, nullable = true)
	private Date productionTime;
	
	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name="robot_id")
	private Company company;
	
	@OneToMany(mappedBy="robot")
	private Set<Weapon> weapons;
	
	@ManyToMany(cascade = CascadeType.ALL)
	@JoinTable(name = "robot_sensor",  joinColumns = { 
			@JoinColumn(name = "robot_id") }, 
			inverseJoinColumns = { @JoinColumn(name = "sensor_id"
					) })
	private Set<Sensor> sensors;
	
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSerial() {
		return serial;
	}
	public void setSerial(String serial) {
		this.serial = serial;
	}
	public Date getProductionTime() {
		return productionTime;
	}
	public void setProductionTime(Date productionTime) {
		this.productionTime = productionTime;
	}
	public Company getCompany() {
		return company;
	}
	public void setCompany(Company company) {
		this.company = company;
	}
	public Set<Weapon> getWeapons() {
		return weapons;
	}
	public void setWeapons(Set<Weapon> weapons) {
		this.weapons = weapons;
	}
	public Set<Sensor> getSensors() {
		return sensors;
	}
	public void setSensors(Set<Sensor> sensors) {
		this.sensors = sensors;
	}
}

Tutaj również najistotniejsza jest anotacja @ManyToMany oraz @JoinTable w której wskazujemy klucze według których łączymy nasze tabele. Dodajemy mapowanie nowej klasy do konfiguracji.
 

<mapping class="com.binaryalchemist.killer_robot.Sensor" />

Cały kod prezentuję się następująco:
 

@WebServlet(
name="robotservlet",
urlPatterns={"/robot","/" })
public class KillerRobotServlet extends HttpServlet {
	
	public KillerRobotServlet() {
		super();
	}
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		KillerRobot robi = new KillerRobot();
		robi.setName("Bob");
		robi.setProductionTime(new Date());
		robi.setSerial("AX7N41S");
		
		Company company = new Company();
		company.setName("Skynet");
		company.setAdress("USA");
		company.setWebpage("www.skynet.com");
		company.setFounded(new Date());
		robi.setCompany(company);
		company.setRobot(robi);
		
		Set<Weapon> weapons = new HashSet<Weapon>(); 
		
		Weapon weapon1 = new Weapon();
		weapon1.setName("Laser");
		weapon1.setType("Energy");
		weapon1.setFirepower(36);
		weapon1.setWeight(56.6f);
		weapon1.setRobot(robi);
		Weapon weapon2 = new Weapon();
		weapon2.setName("Gattling Gun");
		weapon2.setType("Projectile");
		weapon1.setFirepower(58);
		weapon2.setWeight(172.6f);
		weapon2.setRobot(robi);
		weapons.add(weapon1);
		weapons.add(weapon2);
		
		robi.setWeapons(weapons);
		
		KillerRobot robi2 = new KillerRobot();
		robi2.setName("Jack");
		robi2.setProductionTime(new Date());
		robi2.setSerial("Z9J6HSA");
		
		Sensor sensor = new Sensor();
		sensor.setName("Optical sensor");
		sensor.setType("Image Sensor");
		sensor.setDescription("Sensor for finding objects by robot");
		Sensor sensor2 = new Sensor();
		sensor2.setName("Ultrasonic Range Finder");
		sensor2.setType("Sound Sensor");
		sensor2.setDescription("Sensor for detecting ultrasonic waves");
		
		Set<Sensor> sensors = new HashSet<Sensor>();
		sensors.add(sensor);
		sensors.add(sensor2);
		
		robi.setSensors(sensors);
		robi2.setSensors(sensors);
		
		
		Session session = DBConnection.getSession();
		session.beginTransaction();
		session.save(robi);
		session.save(robi2);
		session.save(weapon1);
		session.save(weapon2);
		

		session.getTransaction().commit();
		DBConnection.close();
		
		
		request.getRequestDispatcher("/index.jsp").forward(request, response);
	}
}

Na koniec pokazane zostanie jak przeszukiwać, aktualizować i usuwać dane za pomocą Hibernate ORM.
 

HQL w Hibernnate:

W Hibernacie możemy korzystać z zwykłych zapytań SQL jak również przy użyciu HQL który jest bardzo podobny do języka SQL lecz zapytania wykonujemy na obiektach niż jak w przypadku SQL na tabelach.
 
Poniżej zamieszczone są podstawowe przykład czytania, aktualizowania i usuwania danych przy wykorzystaniu HQL. Operacje wykonywane są na podstawie danych które stworzyliśmy wcześniej w tym instruktażu.
 

		Session session = DBConnection.getSession();
		session.beginTransaction();

		//Finding all robots from database
		Query query = session.createQuery("from KillerRobot");
		List<KillerRobot> robotList = query.list();
		for(KillerRobot robot : robotList){
			System.out.println("Robot Data: ID "+robot.getId()+", Name "+robot.getName()+", Serial "+robot.getSerial());
		}
		
		//Update robot data
		query = session.createQuery("update KillerRobot set name= :name where robot_id= :robot_id");
		query.setParameter("name", "T-1000");
		query.setLong("robot_id", 1);
		query.executeUpdate();

		//Delete robot from DB
		query = session.createQuery("delete from KillerRobot where robot_id= :robot_id");
		query.setLong("robot_id", 2);
		query.executeUpdate();
		
		session.getTransaction().commit();
		DBConnection.close();

Cały kod źródłowy stworzony w Eclipse jest poniżej możliwy do ściągnięcia.
Kod zródłowy

Dodaj komentarz

WordPress Video Lightbox Plugin