В настоящее время мы проводим обзор производительности и улучшаем функцию DB CRUD приложения, и одним из обзоров было использование пула соединений Hikari и более поздней версии Hibernate и Hibernate-HikariCP для наших тестов.
Мы выполняем это путем обновления версии Hibernate с 5.2.10 до 5.2.16 с Hikari версии 3.1.0. Все это упражнение было протестировано с Tomcat 8 и MSSQL 2016.
Для простоты мы проверили один простой поиск с одним условием поиска, используя сущность Пользователь.
Конфигурация Hibernate XML:
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
<property name="show_sql">true</property>
<!-- Hikari specific properties -->
<property name="hibernate.connection.provider_class">com.zaxxer.hikari.hibernate.HikariConnectionProvider</property>
<property name="hikari.maximumPoolSize">30</property>
<property name="hikari.idleTimeout">300000</property>
<property name="hikari.maxLifetime">1800000</property>
<!-- Database connection properties -->
<property name="hibernate.hikari.dataSourceClassName">com.microsoft.sqlserver.jdbc.SQLServerDataSource</property>
<property name="hibernate.connection.url">jdbc:sqlserver://192.168.0.102:1433;databaseName=migration1;user=sun;password=p@ssw0rd;</property>
<property name = "hibernate.connection.username">sun</property>
<property name = "hibernate.connection.password">p@ssw0rd</property>
<mapping class="com.hib.model.Outlet"/>
<mapping class="com.hib.model.Receipt"/>
<mapping class="com.hib.model.Cashier"/>
<mapping class="com.hib.model.Category"/>
<mapping class="com.hib.model.Item"/>
<mapping class="com.hib.model.PayMethodName"/>
<mapping class="com.hib.model.Pos"/>
<mapping class="com.hib.model.Purchase"/>
<mapping class="com.hib.model.User"/>
</session-factory>
</hibernate-configuration>
Класс HibernateUtil довольно прост, в настоящее время выглядит так:
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
Configuration configuration = new Configuration().configure();
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
sessionFactory = new Configuration().configure().buildSessionFactory(builder.build());
} catch (Throwable th) {
System.err.println("Initial SessionFactory creation failed " + th);
throw new ExceptionInInitializerError(th);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
Пользовательский объект, аннотированный, исключая геттер / установщики:
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UpdateTimestamp;
@Entity
@Table(name = "User")
@XmlRootElement
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "id")
@NotNull
@GeneratedValue(generator="`id`")
@GenericGenerator(name="`id`", strategy = "increment")
private Integer id;
@NotNull
@Column(name = "FullName")
private String fullName;
@Column(name = "NickName")
private String nickName;
@Column(name = "Tel")
private String tel;
@Column(name = "Rank")
private String rank;
@NotNull
@Column(name = "Email")
private String email;
@NotNull
@Column(name = "Password")
private String password;
@Lob
@Column(name = "PhotoUpload")
private String photoUpload;
@Version
@Column(name = "CreateDate")
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date createDate;
@Version
@Column(name = "EditDate")
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date editDate;
@Column(name = "CreateBy")
private String createBy;
@Column(name = "ActiveInd")
private String activeInd;
@Column(name = "LastLogin")
@Temporal(TemporalType.TIMESTAMP)
private Date lastLogin;
@Column(name = "tenant_id")
private String tenantId;
@NotNull
@Column(name = "group_id")
private String groupId;
public User() {
}
public User(Integer id, String fullName,String nickName,String tel,String rank,String email,String password,String photoUpload,Date createDate,Date editDate,String createBy,String activeInd,Date lastLogin, String tenantId, String groupId) {
this.id = id;
this.fullName = fullName;
this.nickName = nickName;
this.tel =tel;
this.rank=rank;
this.email =email;
this.password = password;
this.photoUpload = photoUpload;
this.createDate =createDate;
this.editDate =editDate;
this.createBy =createBy;
this.activeInd = activeInd;
this.lastLogin=lastLogin;
this.tenantId= tenantId;
this.groupId =groupId;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof User)) {
return false;
}
User other = (User) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.example.dao.User[ id=" + id + " ]";
}
Функция поиска конкретных пользователей в этом методе класса обслуживания пользователей:
@Override
public List<User> getUserById(Integer id) {
List<User> result = null;
Session session = getSession();
try {
Criteria criteria = getSession().createCriteria(User.class);
Criteria criteria = getSession().(User.class);
criteria.add(Restrictions.eq("id", id));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
result = query.getResultList();
}catch (HibernateException e){
userLogger.error("Error found: " + e);
}finally {
if (session != null)
session.close();
}
return result;
}
При проверке, если используется исходный пул соединений C3P0, функция поиска, приведенная выше, вернет ожидаемый результат сопоставления записи, соответствующей переданному номеру Id (числовое значение).
Однако при переключении на HikariCP результат возвращает ноль. Однако если мы напишем NativeQuery вместо Criteria, это не будет затронуто.
Впоследствии мы попробовали три варианта, чтобы посмотреть, работает ли он, но с разными ошибками:
Вариант 1: - возвращает исключение IllegalArgumentException, сущность Пользователь не отображается
TypedQuery<User> query = session.createQuery("select u from "+User.class.getSimpleName()+" u",User.class) ;
result = query.getResultList();
Вариант 2: - возвращает исключение IllegalArgumentException, а не сущность Пользователь.
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root);
Query<User> q = session.createQuery(cq);
result = q.getResultList();
Вариант 3 - возвращает исключение сопоставления, неизвестная сущность Пользователь:
String sql = "SELECT * FROM USER_LOG WHERE id = :number_id";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(User.class);
query.setParameter("number_id", id);
result = query.list();
Поскольку HikariCP сделал много улучшений для других собственных запросов, мы почувствовали, что будет лучше, если мы все еще сможем продолжать использовать этот CP в пользу пула C3P0. Однако мы все еще застряли, поскольку не уверены, какой подход в методе класса Service лучше всего использовать.
Мы ценим любые советы по этому вопросу. Спасибо.