Açıklaması şöyle
org.hibernate.cache.spi.RegionFactory defines the integration between Hibernate and a pluggable caching provider. hibernate.cache.region.factory_class is used to declare the provider to use.
org.hibernate.cache.spi.RegionFactory defines the integration between Hibernate and a pluggable caching provider. hibernate.cache.region.factory_class is used to declare the provider to use.
@Entitypublic class Doctor {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;@OneToMany(mappedBy = "doctor", fetch = FetchType.LAZY)@org.hibernate.annotations.BatchSize(size = 5)private Collection<Appointment> appointments;}
Whenever Hibernate initializes one Doctor – Appointments collection it will also initialize Appointments for four more Doctors. The query which Hibernate generates is as follows.SELECT appointmen0_.doctor_id,appointmen0_.id,appointmen0_.id,appointmen0_.appointmentTime,appointmen0_.doctor_idFROM AppointmentWHERE appointmen0_.doctor_id IN (?, ?, ?, ?, ?)In our example of 10 doctors, a ceiling of N + 1 / @BatchSize, which is 10 + 1 / 5 = 3 total queries will be executed to fetch Doctors and all the Appointments. It’s not really a great optimization as still more queries are executed than we would like, but it is some improvement.Please also bear in mind that it’s a global optimization. Once this annotation is present, referring to one of our Doctor’s appointments will result preloading Appointments for another four Doctors.
Since Hibernate 5.2.18, you can use the MetadataBuilderContributor utility to customize the MetadataBuilder even if you are bootstrapping via JPA.
import org.hibernate.boot.MetadataBuilder;import org.hibernate.boot.spi.MetadataBuilderContributor; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.type.BooleanType; public class SQLFunctionContributor implements MetadataBuilderContributor { @Override public void contribute(MetadataBuilder metadataBuilder) { metadataBuilder.applySqlFunction("json_contains_key", new SQLFunctionTemplate(BooleanType.INSTANCE, "JSON_CONTAINS(?1, JSON_QUOTE(?2))")); } }
In the above snippet, I am overriding the contribute method by providing one of the MySQL JSON functions.The JSON_CONTAINS() function checks whether one JSON document contains another JSON document.JSON_CONTAINS(target_json, candidate_json)Parameterstarget_jsonRequired. A JSON document.candidate_jsonRequired. The included JSON document.As you see, I have created a custom key “json_contains_key”. This actually will be used in Criteria API or Query DSL and will be parsed by Hibernate as JSON_CONTAINS().And provide the custom MetadataBuilderContributor to Hibernate via the hibernate.metadata_builder_contributor configuration property.spring.jpa.properties.hibernate.metadata_builder_contributor=com.abhicodes.customdialect.util.SQLFunctionContributor
import org.hibernate.event.spi.PostDeleteEventListener;
public class HibernateCDCEventListener implements PostDeleteEventListener,PostUpdateEventListener, PostInsertEventListener { public HibernateCDCEventListener(EntityManagerFactory entityManagerFactory) { SessionFactoryImplementor sessionFactory = entityManagerFactory .unwrap(SessionFactoryImplementor.class); EventListenerRegistry eventListenerRegistry = sessionFactory.getServiceRegistry() .getService(EventListenerRegistry.class); eventListenerRegistry.appendListeners(POST_DELETE, this); eventListenerRegistry.appendListeners(POST_UPDATE, this); eventListenerRegistry.appendListeners(POST_INSERT, this); } @Override public void onPostDelete(final PostDeleteEvent event) { // Handle delete events } @Override public void onPostInsert(final PostInsertEvent event) { processEntity(event.getEntity(), event.getEntity(), event.getEntity()); } @Override public void onPostUpdate(final PostUpdateEvent event) { processEntity(event.getEntity(), event.getEntity(), event.getEntity()); } @Override public boolean requiresPostCommitHanding(final EntityPersister persister) { return false; } }
@Indexed
@Entity
@Table
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Plant {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@FullTextField()
@NaturalId
private String name;
@FullTextField
@NaturalId
private String scientificName;
@FullTextField
private String family;
private Instant createdAt ;
}import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;we annotate the fields we want to search on with the @FullTextField annotation. This annotation works only for string fields, but others exist for fields of different types.
import org.hibernate.annotations.NaturalId;import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;@Indexed@Entity@Table(name = "plant")public class Plant {@FullTextField@NaturalIdprivate String name;@FullTextField@NaturalIdprivate String scientificName;@FullTextFieldprivate String family;}
import org.hibernate.validator.constraints.Length;import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
public record EnterpriseRecord(String id,
@NotNull String name,
@NotNull @Length(min = 2, max = 255) String address) {
}Foo foo = ...;
StatelessSession statelessSession = ...;
Transaction transaction = statelessSession.beginTransaction();
statelessSession.insert(foo);
transaction.commit(); StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
ScrollableResults articles = session.getNamedQuery("GetArticles")
.scroll(ScrollMode.FORWARD_ONLY);
while ( articles.next() ) {
Article article = (Article) articles(0);
article.updateName(newArticleName);
session.update(article);
}
tx.commit();
session.close();import org.hibernate.search.MassIndexer;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.massindexing.MassIndexer;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
@Transactional
@Component
public class Indexer {
private EntityManager entityManager;
private static final int THREAD_NUMBER = 4;
public Indexer(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void indexPersistedData(String indexClassName) throws IndexException {
try {
SearchSession searchSession = Search.session(entityManager);
Class<?> classToIndex = Class.forName(indexClassName);
MassIndexer indexer = searchSession
.massIndexer(classToIndex)
.threadsToLoadObjects(THREAD_NUMBER);
indexer.startAndWait();
} catch (Exception e) {
...
}
}Şöyle yaparız@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ApplicationRunner buildIndex(Indexer indexer) throws Exception {
return (ApplicationArguments args) -> {
indexer.indexPersistedData("com.mozen.springboothibernatesearch.model.Plant");
};
}
}@Configurationpublic class HibernateSearchIndexBuild implements ApplicationListener<ApplicationReadyEvent>{@Autowiredprivate EntityManager entityManager;@Override@Transactionalpublic void onApplicationEvent(ApplicationReadyEvent event) {SearchSession searchSession = Search.session(entityManager);MassIndexer indexer = searchSession.massIndexer()
.idFetchSize(150)
.batchSizeToLoadObjects(25).threadsToLoadObjects(12);try {indexer.startAndWait();} catch (InterruptedException e) {...}}}
Hibernate Search provides you with both Lucene and ElasticSearch implementations that are highly optimized for full-text search.
SpringBoot İle Kullanım<dependency><groupId>org.Hibernate.search</groupId> <artifactId>Hibernate-search-mapper-orm</artifactId> <version>6.0.2.Final</version> </dependency> <dependency> <groupId>org.Hibernate.search</groupId> <artifactId>Hibernate-search-backend-lucene</artifactId> <version>6.0.2.Final</version> </dependency>
spring-boot-hibernate-search
# HIBERNATE SEARCHspring.jpa.properties.hibernate.search.backend.directory.root=/home/indexes/
server:
port: 9000
spring:
datasource:
url: jdbc:h2:mem:mydb
username: mozen
password: password
jpa:
open-in-view: false
properties:
hibernate:
search:
backend:
type: lucene
directory.root: ./data/index@NoRepositoryBean public interface SearchRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { List<T> searchBy(String text, int limit, String... fields); }searchBy metodunun gerçekleştirimini ekleriz. Spring kuralları gereği interface ismine Impl son ekini vererek yeni bir sınıf yaratırız. Burada SearchSession hibernate sınıfı. Burada fetch() metoduna limit değeri geçiliyor.
import org.hibernate.search.mapper.orm.session.SearchSession; @Transactional public class SearchRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements SearchRepository<T, ID> { private final EntityManager entityManager; public SearchRepositoryImpl(Class<T> domainClass, EntityManager entityManager) { super(domainClass, entityManager); this.entityManager = entityManager; } public SearchRepositoryImpl( JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityManager = entityManager; } @Override public List<T> searchBy(String text, int limit, String... fields) { SearchResult<T> result = getSearchResult(text, limit, fields); return result.hits(); } private SearchResult<T> getSearchResult(String text, int limit, String[] fields) { SearchSession searchSession = Search.session(entityManager); SearchResult<T> result = searchSession .search(getDomainClass()) .where(f -> f.match().fields(fields).matching(text).fuzzy(2)) .fetch(limit); return result; } }
@Repository
public interface PlantRepository extends SearchRepository<Plant, Long> {
}@Data @AllArgsConstructor @NoArgsConstructor public class PageDTO<T> { private List<T> content; private long total; } @Override public PageDTO<T> searchPageBy(String text, int limit, int offset, String... fields) { SearchResult<T> result = getSearchResult(text, limit, offset, fields); return new PageDTO<T>(result.hits(), result.total().hitCount()); } private SearchResult<T> getSearchResult(String text, int limit, int offset, String[] fields) { SearchSession searchSession = Search.session(entityManager); SearchResult<T> result = searchSession .search(getDomainClass()) .where(f -> f.match().fields(fields).matching(text).fuzzy(2)) .fetch(offset, limit); return result; }
@Entitypublic class Account {@Id@GenericGenerator(name = "account_id_generator",strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",parameters = {@Parameter(name = "sequence_name", value = "account_id_seq"),@Parameter(name = "increment_size", value = "50"),@Parameter(name = "optimizer", value = "pooled-lo")})@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "account_id_generator")private Long id;...}
Suppose, we have four tables (table1,table3,table4) each having a column “date”. I want to get list of max date for each table in a single query.The required output should be likeid tablename max1 table1 2020–03–252 table2 2020–04–303 table3 2020–02–284 table4 2020–03–31
Hibernate ile şöyle yaparızselect row_number() over() as id, * from (select ‘table1’ as tablename, max(date) from table1unionselect ‘table2’ as tablename, max(date) from table2unionselect ‘table3’ as tablename, max(date) from table3unionselect ‘table4’ as tablename, max(date) from table4order by tablename) t1;
import java.time.LocalDate;import javax.annotation.concurrent.Immutable;import javax.persistence.Entity;import javax.persistence.Id;import org.hibernate.annotations.Subselect;import org.hibernate.annotations.Synchronize;@Entity@Subselect( “select row_number() over() as id, * from (" +
"select ‘table1’ as tablename, max(date) from table1 “ +“union " +
"select ‘table2’ as tablename, max(date) from table2 " +
"union " +
"select ‘table3’ as tablename, max(date) from table3 ” +“union " +
"select ‘table4’ as tablename, max(date) from table4 order by tablename) t1”)@Synchronize({“table1”, “table2”,”table3",”table4"})@Immutablepublic class TableStatus {@Idprivate Long id;private String tablename;private LocalDate max;}@Repositorypublic interface TableStatusRepository extends JpaRepository<TableStatus,Long>{}
Hibernate doesn’t know which database tables are used by the SQL statement configured in the @Subselect annotation. You can provide this information by annotating the entity with @Synchronize. That enables Hibernate to flush pending state transitions on tables table1, table2,table3 and table4 entities before selecting a TableStatus entity.
Even if it is best to adhere to the JPA standard, in reality, many JPA providers offer additional features targeting a high-performance data access layer requirements.Örnek
For this purpose, Hibernate comes with the following non-JPA compliant features:
- extended identifier generators (hi/lo, pooled, pooled-lo)
- transparent prepared statement batching
- customizable CRUD (@SQLInsert, @SQLUpdate, @SQLDelete) statements
- static/dynamic entity/collection filters (e.g. @FilterDef, @Filter, @Where)
- mapping attributes to SQL fragments (e.g. @Formula)
- immutable entities (e.g. @Immutable)
- more flush modes (e.g. FlushMode.MANUAL, FlushMode.ALWAYS)
- querying the second-level cache by the natural key of a given entity
- entity-level cache concurrency strategies
- (e.g. Cache(usage = CacheConcurrencyStrategy.READ_WRITE))
- versioned bulk updates through HQL
- exclude fields from optimistic locking check (e.g. @OptimisticLock(excluded = true))
- versionless optimistic locking
- support for skipping (without waiting) pessimistic lock requests
- support for multitenancy
@Formula("(select current_date())")
Date currentDate;
Örnek@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(generator = "roles_id_seq", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "roles_id_seq", sequenceName = "roles_id_seq",
allocationSize = 1)
@Column(name = "id")
private Long id;
...
@Formula("(SELECT COUNT(*) FROM user_roles us WHERE us.id_role = id)")
private Integer countUsing;
}
Örnek@MappedSuperclass
public abstract class BaseEntity {
@Id
protected String id;
@Formula("(select d from SOME_TABLE ST ST.some_id = id)")
private Long property4;
}