SoFunction
Updated on 2025-03-09

The principles and configuration of Hibernate

The “Hello World” and “Hello world” sample program give you a simple understanding of Hibernate.
Understanding the architecture of Hibernate introduces the main functions of the Hibernate interface.
Core interface Hibernate has 5 core interfaces through which developers can store and obtain persistent objects and can perform transaction control.
An important term: TypeType is a term invented by the inventor of Hibernate. It is a very basic and powerful element in the entire architecture. A Type object can map a Java type to a field in a table in a database.
The policy interface Hibernate is different from some other open source software - its high degree of scalability, which is achieved through its built-in policy mechanism.
The basic configuration Hibernate can be configured to run in any Java environment. Generally speaking, it is usually used in C/S mode projects in layer 2-3 and deployed on the server.
Create a SessionFactory object To create a SessionFactory object, an instance of the Configuration class must be created when Hibernate is initialized and the written mapping file must be handed over to it.

“Hello World”

The Hibernate application defines some persistent classes and defines the mapping relationship between these classes and database tables. Our "Hello world" sample program contains a class and a mapping file. Let's see what this simple persistent class contains? How are mapping files defined? In addition, how should we use Hibernate to operate this persistent class.

The purpose of our simple example program is to store some persistent classes in the database, then retrieve them from the database, and display their information body to the user. Message is a simple persistent class: which contains the information we want to display, and its source code is as follows:

List 1 A simple persistent class

package hello;
public class Message {
private Long id;
private String text;
private Message nextMessage;
private Message() {}
public Message(String text) {
= text;
}
public Long getId() {
return id;
}
private void setId(Long id) {
= id;
}
public String getText() {
return text;
}
public void setText(String text) {
= text;
}
public Message getNextMessage() {
return nextMessage;
}
public void setNextMessage(Message nextMessage) {
= nextMessage;
}
}

The Message class has three properties: the id of the Message, the message body, and a pointer to the next message. The id attribute allows our application to uniquely identify this message. It is usually equivalent to the primary key in the database. If multiple instance objects of the Message class have the same id, they represent the same record in a table in the database. Here we chose the long integer as our id value, but this is not required. Hibernate allows us to use any type as the object's id value, which we will describe in detail later.

You may have noticed that the code of the Message class is similar to that of JavaBean, and that it has a constructor without parameters, which I will continue to use in this style to write the code of the persistent class in our future code.

Hibernate will automatically manage instances of the Message class and persist through internal mechanisms. However, in fact, the Message object does not implement any class or interface about Hibernate, so we can also use it as a normal Java class:

Message message = new Message("Hello World");
( () );

The above code is exactly what we expect: it prints "hello world" to the screen. But this is not our ultimate goal; in fact, Hibernate is very different from environments such as EJB containers in the way they are implemented in the persistence layer. Our persistent class (Message class) can be used in container-independent environments, unlike EJB that requires an EJB container to be executed. To express this more clearly, the following code saves a new message to the database:

Session session = getSessionFactory().openSession();
Transaction tx = ();
Message message = new Message("Hello World");
(message);
();
();

The above code calls Hibernate's Session and Transaction interfaces (we will mention it immediately about the getSessionFactory() method). It is equivalent to executing the following SQL statement:

insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)
values (1, 'Hello World', null)

In the above SQL statement, what value is the MESSAGE_ID field initialized to? Since we did not assign the initial value to the id attribute of the message object in the previous code, is it null? In fact, Hibernate has made special treatments for the id attribute: since it is a unique identifier of an object, when we make a save() call, Hibernate will automatically assign it a unique value (we will talk about how it generates this value in the following content).

Let's assume that you have created a table named MESSAGE in the database. Since the previous code lets us store the Message objects into the database, we will now take them out one by one. The following code will take out all Message objects in the database in alphabetical order and print their message body to the screen:

Session newSession = getSessionFactory().openSession();
Transaction newTransaction = ();
List messages =("from Message as m order by asc");
( () + " message(s) found:" );
for ( Iterator iter = (); (); ) {
Message message = (Message) ();
( () );
}
();
();

In the above code, you may be troubled by this parameter of the find() method: "from Message as m order by asc", which is actually a query language defined by Hibernate itself, and its full name is Hibernate Query Language (HQL). In layman's terms, the relationship between HQL and SQL is almost the same as the relationship between dialect and Mandarin. At first glance, you will think it is a bit similar to SQL statements. In fact, when find() is called, Hibernate will translate this HQL language into the following SQL statement:

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID
from MESSAGES m
order by m.MESSAGE_TEXT asc

The following are the running results:

1 message(s) found:
Hello World

If you don't have ORM development experience before, you may want to look for this SQL statement somewhere in your code, but in Hibernate you may be disappointed: it doesn't exist at all! All SQL statements are dynamically generated by Hibernate.

Maybe you will feel that you are still short of something, right! Hibernate cannot persist our Message class by using the above code. We need some more information, and that's the mapping definition table! This table is embodied in XML format in Hibernate. It defines how the properties of the Message class correspond to the fields of the MESSAGES table in the database. List 2 is the list of mapping configuration files for this example program:

List 2: Object-Relationship Map of Sample Program

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"/hibernate-mapping-2.">
<hibernate-mapping>
<class name="" table="MESSAGES">
<id name="id" column="MESSAGE_ID">
<generator class="increment"/>
</id>
<property name="text" column="MESSAGE_TEXT"/>
<many-to-one name="nextMessage" cascade="all" column="NEXT_MESSAGE_ID"/>
</class>
</hibernate-mapping>

The above document tells Hibernate how to map the Message class to the MESSAGES table, where the id attribute of the Message class corresponds to the MESSAGE_ID field of the table, the text attribute corresponds to the MESSAGE_TEXT field of the table, and the nextMessage attribute is a many-to-one relationship, which corresponds to the NEXT_MESSAGE_ID in the table.

Compared with some open source projects, Hibernate's configuration file is actually very easy to understand. You can easily modify and maintain it. As long as you define the correspondence between the persistent class and the table fields in the database, Hibernate will automatically generate SQL statements to insert, update, delete, and search for Message objects. You can do not write a SQL statement, or even do not need to understand the SQL language!

Now let's do a new experiment. We first take out the first Message object, then modify its message body. Finally, we generate a new Message object and use it as the next message of the first Message object. The code is as follows:

List 3 Update a message

Session session = getSessionFactory().openSession();
Transaction tx = ();
// 1 is the generated id of the first message
Message message =(Message) ( , new Long(1) );
("Greetings Earthling");
Message nextMessage = new Message("Take me to your leader (please)");
( nextMessage );
();
();

When calling the above code, Hibernate automatically generates the following SQL statements:

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID
from MESSAGES m
where m.MESSAGE_ID = 1

insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)
values (2, 'Take me to your leader (please)', null)

update MESSAGES
set MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2
where MESSAGE_ID = 1

When the text attribute and nextMessage of the first Message object are modified by the program, please note how Hibernate detects this change and updates it automatically in the database. This is actually a valuable feature of Hibernate. We call it "automatic dirty data detection". This feature of Hibernate allows us to modify the properties of a persistent object without explicitly notifying Hibernate to update it in the database. Similarly, when the first Message object calls the setNextMessage() method to reference the second Message object as its next message, the second message can be automatically saved in the database without calling the save() method. This feature is called "cascade saving", and it also saves us from the pain of explicitly calling the save() method on the second Message object.

If we run the previous section of code that prints all Message objects in the database, the running result is as follows:

2 message(s) found:
Greetings Earthling
Take me to your leader (please)


The “Hello world” sample program is now introduced. We finally have a simple understanding of Hibernate. Let's look back and give a brief introduction to Hibernate's main API calls:


 Understand Hibernate's architecture

When you want to use Hibernate to develop your own persistence layer-based application, the first thing you should do is to be familiar with its programming interface. Hibernate's API interface is designed as simple and clear as possible to facilitate developers. However, in fact, due to the complexity of ORM, its APIs generally cannot be designed very simply. But don't worry, you don't need to understand all the Hibernate API interfaces all at once.

We put the application layer on the top of the persistence layer. In fact, in traditional projects, the application layer acts as a client role of the persistence layer. But for some simple projects, the application layer and the persistence layer are not so clearly distinguished, and that's nothing, in this case you can merge the application layer and the persistence layer into one layer.

Hibernate's interface can be roughly divided into the following types:

· Some interfaces called by user's applications to complete basic creation, reading, updating, deleting operations and query operations. These interfaces are the main interfaces for Hibernate to implement the business logic of user programs, and they include Session, Transaction, and Query.

Hibernate is used to read configuration files such as mapping tables. Typical representatives are Configuration classes.

· Callback interface. It allows applications to perform corresponding operations on the occurrence of some events, such as Interceptor, Lifecycle and Validatable, all of which are interfaces of this type.

· Some interfaces that can be used to extend Hibernate's mapping mechanism, such as UserType, CompositeUserType, and IdentifierGenerator. These interfaces can be implemented by user programs (if necessary).

Hibernate uses the following technologies in the J2EE architecture: JDBC, JTA, and JNDI. Among them, JDBC is a basic layer that supports relational database operations; it is combined with JNDI and JTA, so that Hibernate can be easily integrated into J2EE application server.

Here, we will not discuss all the methods in the Hibernate API interface in detail. We will only briefly talk about the functions of each main interface. If you want to know more, you can view the source code of these interfaces in the subpackages in the Hibernate source code package. Let's talk about all the main interfaces in turn:

  Core interface

The following 5 core interfaces will be used in almost any actual development. Through these interfaces, you can not only store and obtain persistent objects, but also perform transaction control.

Session interface

The Session interface is the most important interface for Hibernate developers. However, in Hibernate, the instantiated Session is a lightweight class, and creating and destroying it will not take up a lot of resources. This is indeed very important in actual projects, because in client programs, Session objects may be continuously created and destroyed. If the session overhead is too high, it will have adverse effects on the system. But it is worth noting that the Session object is non-thread-safe, so in your design it is best to create only one Session object for a thread.

In the minds of Hibernate designers, they view session as an intermediate interface between data connection and transaction management. We can imagine the session as a buffer of persistent objects. Hibernate can detect changes in these persistent objects and refresh the database in time. We sometimes call Session a persistence layer manager because it contains operations related to these persistence layer, such as storing persistent objects to the database and obtaining them from the database. Please note that Hibernate's session is different from HttpSession in JSP applications. When we use the term session, we refer to the session in Hibernate, and we will later refer to the HttpSesion object as the user session.

SessionFactory interface

Here is a design pattern - factory mode, where the user program obtains a Session instance from the factory class SessionFactory.

What makes you wonder is that SessionFactory is not lightweight! In fact, its designer's intention is to make it shareable throughout the application. Typically, a project usually requires only one SessionFactory, but when your project wants to operate multiple databases, you must specify a SessionFactory for each database.
SessionFactory actually plays a buffer in Hibernate, which buffers SQL statements automatically generated by Hibernate and some other mapping data, and also buffers some data that may be reused in the future.

Configuration interface

The function of the Configuration interface is to configure and start Hibernate. During the startup of Hibernate, an instance of the Configuration class first locates the location of the mapping document, reads these configurations, and then creates a SessionFactory object.

Although the Configuration interface plays a small role in the entire Hibernate project, it is every object you encounter when starting hibernate.

Transaction interface

The Transaction interface is an optional API, you can choose not to use this interface, instead of the underlying transaction code written by Hibernate's designers themselves. The Transaction interface is an abstraction of actual transaction implementations, including transactions of JDBC, UserTransaction in JTA, and even CORBA transactions. The reason for this design is that developers can use a unified transaction operation interface, so that their projects can easily move values ​​between different environments and containers.

Query and Criteria interfaces

The Query interface allows you to easily query databases and persistent objects. It can have two ways of expression: HQL language or SQL statements in local databases. Query is often used to bind query parameters, limit the number of query records, and ultimately perform query operations.

The Criteria interface is very similar to the Query interface, which allows you to create and execute object-oriented standardized queries.

It is worth noting that the Query interface is also lightweight and cannot be used outside of Session.

Callback interface

When some useful events occur, such as loading, storing, and deleting persistent objects, the Callback interface will notify Hibernate to receive a notification message. Generally speaking, the Callback interface is not necessary in user programs, but you may use it when you want to create audit logs in your project.

An important term: Type

Hibernate's designers invented a term: Type, which is a very basic and powerful element throughout the architecture. A Type object can map a Java type to fields in a table in a database (actually, it can be mapped to multiple fields in a table). All properties of a persistent class correspond to a type. This design idea uses Hibernate with high flexibility and scalability.

Hibernate has many built-in type types, including almost all Java basic types, such as, byte[] and.

Not only that, Hibernate also supports user-defined types. By implementing the interface UserType and the interface CompositeUserType, you can add your own type. You can use this feature to use custom types such as Address and Name in your project, so that you can get greater convenience and make your code more elegant. Custom type is a core feature in Hibernate, and its designers encourage you to use it more to create a flexible and elegant project!

Policy interface

Hibernate is different from some other open source software - its high degree of scalability, which is achieved through its built-in policy mechanism. When you feel that some of Hibernate is insufficient or have some flaws, you can develop your own policy to replace it, and all you have to do is inherit its policy interface and then implement your new policy. Here is its policy interface:

· Primary key generation (IdentifierGenerator interface)

·Native SQL language support (Dialect abstract class)

· Buffer mechanism (Cache and CacheProvider interfaces)

· JDBC connection management (ConnectionProvider interface)

· Transaction Management (TransactionFactory, Transaction, and TransactionManagerLookup interfaces)

· ORM policy (ClassPersister interface)

· PropertyAccessor interface

· Creation of proxy objects (ProxyFactory interface)

Hibernate creates a default implementation for the mechanisms listed above, so if you just want to enhance the functionality of a certain strategy, you just need to simply inherit this class, and there is no need to write code from scratch.

The above are some of the core interfaces of Hibernate, but when we really start developing with it, there may always be a question in your mind: What method did I use and where did I get the Session? Let's answer this question below.

Basic configuration

Now let's review what we've done before: we wrote a sample program and briefly explained some of the core classes of Hibernate. But to really make your project run, there is one more thing you have to do: configuration. Hibernate can be configured to run in any Java environment. Generally speaking, it is usually used in C/S mode projects in layer 2-3 and deployed on the server. In such projects, a web browser, or a Java GUI program, acts as the client. Although our focus is mainly on multi-layer web applications, Hibernate can actually be used in some command-line-based applications. Moreover, the configuration of Hibernate will be different in different environments. Hibernate runs in two environments: manageable environment and unmanageable environment.

· Manageable environment - This environment can manage the following resources: pool resource management, such as database connection pools and, as well as transaction management and security definitions. Some typical J2EE servers (JBoss, Weblogic, WebSphere) have implemented these.

Unmanageable environments - just provide some basic features such as servlet container environments like Jetty or Tomcat. An ordinary Java desktop application or command line program can also be considered to be in this environment. This environment cannot provide automatic transaction processing, resource management, or security management, which must be defined by the application itself.

Hibernate designers designed a unified abstract interface for these two environments, so there is only one environment for developers: manageable environments. If the actual project is built in an unmanageable environment such as Tomcat, Hibernate will use its own transaction code and JDBC connection pool to make it a manageable environment.
For manageable environments, Hibernate integrates itself into this environment. For developers, what you have to do is very simple: just create a SessionFactory class from a Configuration class.
Create a SessionFactory object

In order to create a SessionFactory object, you must create an instance of the Configuration class when Hibernate is initialized and hand over the written mapping file to it. In this way, the Configuration object can create a SessionFactory object. When the SessionFactory object is successfully created, the Configuration object is useless, and you can simply abandon it. The following is the sample code:

Configuration cfg = new Configuration();
("hello/");
( () );
SessionFactory sessions = ();

In the above code, the location of this mapping file is relatively special, and it is related to the current classpath. For example, classpath contains the current directory, then the mapping file in the above code can be saved in the hello directory under the current directory.

As a convention, Hibernate's mapping file defaults to its extension. Another convention is to insist on writing a configuration file for each persistent class. Think about it if you write all the persistent class mappings into a separate configuration file, then this configuration file will definitely be very large and not easy to maintain. But another new question arises here: if you write a configuration file for each class, where should so many configuration files be stored?

Hibernate recommends that you save each mapping file in the same directory as the persistent class and have the same name as the persistent class. For example, the Message persistent class in our first example program is placed in the hello directory, and you must store the mapping file named in this directory. Such a persistent class has its own mapping file, avoiding the occurrence of "hell" in struts projects. If you do not follow this rule, you must manually load each mapping file with the addResource() method; but if you follow this rule, you can conveniently load the persistent class and its mapping file with the addClass() method. The following is an example code that reflects this convenience:

SessionFactory sessions = new Configuration()
.addClass()
.addClass()
.addClass()
.setProperties( () )
.buildSessionFactory();

Of course, Hibernate's mapping files have many other configuration options, such as database connection settings, or some settings that can change the running-time behavior of Hibernate. All settings can be very complicated enough to keep you breathless, but don't worry, because Hibernate sets a reasonable default for most values, and you only need to modify a very small portion of the values ​​in these configuration files.

You can modify Hibernate's system configuration parameters in the following ways:

· Pass an instance as a parameter to the setProperties() method of the Configuration class.

· Set the value in Java –Dproperty=value when Hibernate starts.

· Create a configuration file named under the path that can be found in the classpath.

· Create a file named under the path that can be found in the classpath and define the property value in its <property> tag.

The above is a general introduction to Hibernate. If you want to know more, this article is far from enough. I will launch more information about Hibernate one after another. But there is no doubt about it: it is indeed a very excellent persistence layer solution!