Skip to content

Commit

Permalink
Hibernate support
Browse files Browse the repository at this point in the history
Signed-off-by: Avgustin Marinov <[email protected]>
  • Loading branch information
avgustinmm committed Dec 12, 2024
1 parent af50e8c commit 00116c7
Show file tree
Hide file tree
Showing 41 changed files with 1,292 additions and 216 deletions.
3 changes: 3 additions & 0 deletions hawkbit-repository/hawkbit-repository-jpa-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# hawkBit JPA EclipseLink Vendor integration

Implementation of [EclipseLink](http://www.eclipse.org/eclipselink/) JPA vendor.
47 changes: 47 additions & 0 deletions hawkbit-repository/hawkbit-repository-jpa-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<version>${revision}</version>
<artifactId>hawkbit-repository</artifactId>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>hawkbit-repository-jpa-api</artifactId>
<name>hawkBit :: Repository :: JPA API</name>

<properties>
<apt.source.dir>${project.build.directory}/generated-sources/apt/</apt.source.dir>
</properties>

<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-core</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void afterCommit() {
@Override
@SuppressWarnings({ "squid:S1217" })
public void afterCompletion(final int status) {
log.debug("Transaction completed after commit with status {}", status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK");
log.debug("Transaction completed after commit with status {}", status == TransactionSynchronization.STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK");
}

private void afterCommit(final Runnable runnable) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.model;

import java.io.Serial;

import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PostPersist;
import jakarta.persistence.PostRemove;
import jakarta.persistence.PostUpdate;
import jakarta.persistence.Version;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.context.SecurityContextHolder;

/**
* Base hawkBit entity class containing the common attributes.
*/
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities.
@MappedSuperclass
@EntityListeners({ AuditingEntityListener.class, EntityInterceptorListener.class })
public abstract class AbstractJpaBaseEntity implements BaseEntity {

protected static final int USERNAME_FIELD_LENGTH = 64;

@Serial
private static final long serialVersionUID = 1L;

@Setter // should be used just for test purposes
@Getter
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Setter // should be used just for test purposes
@Getter
@Version
@Column(name = "optlock_revision")
private int optLockRevision;

// Audit fields. use property access to ensure that setters will be called and checked for modification
// (touch implementation depends on setLastModifiedAt(1).
@Column(name = "created_by", updatable = false, nullable = false, length = USERNAME_FIELD_LENGTH)
private String createdBy;
@Column(name = "created_at", updatable = false, nullable = false)
private long createdAt;
@Column(name = "last_modified_by", nullable = false, length = USERNAME_FIELD_LENGTH)
private String lastModifiedBy;
@Column(name = "last_modified_at", nullable = false)
private long lastModifiedAt;

@CreatedBy
public void setCreatedBy(final String createdBy) {
this.createdBy = createdBy;
}

@Column(name = "created_by", updatable = false, nullable = false, length = USERNAME_FIELD_LENGTH)
@Access(AccessType.PROPERTY)
public String getCreatedBy() {
return createdBy;
}

@CreatedDate
public void setCreatedAt(final long createdAt) {
this.createdAt = createdAt;
}

@Column(name = "created_at", updatable = false, nullable = false)
@Access(AccessType.PROPERTY)
public long getCreatedAt() {
return createdAt;
}

@LastModifiedBy
public void setLastModifiedBy(final String lastModifiedBy) {
if (this.lastModifiedBy != null && isController()) {
// initialized and controller = doesn't update
return;
}

this.lastModifiedBy = lastModifiedBy;
}

@Column(name = "last_modified_by", nullable = false, length = USERNAME_FIELD_LENGTH)
@Access(AccessType.PROPERTY)
public String getLastModifiedBy() {
return lastModifiedBy == null ? createdBy : lastModifiedBy;
}

@LastModifiedDate
public void setLastModifiedAt(final long lastModifiedAt) {
if (this.lastModifiedAt != 0 && isController()) {
// initialized and controller = doesn't update
return;
}

this.lastModifiedAt = lastModifiedAt;
}

@Column(name = "last_modified_at", nullable = false)
@Access(AccessType.PROPERTY)
public long getLastModifiedAt() {
return lastModifiedAt == 0 ? createdAt : lastModifiedAt;
}

/**
* Defined equals/hashcode strategy for the repository in general is that an entity is equal if it has the same {@link #getId()} and
* {@link #getOptLockRevision()} and class.
*/
@Override
// Exception squid:S864 - generated code
@SuppressWarnings({ "squid:S864" })
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (id == null ? 0 : id.hashCode());
result = prime * result + optLockRevision;
result = prime * result + this.getClass().getName().hashCode();
return result;
}

/**
* Defined equals/hashcode strategy for the repository in general is that an entity is equal if it has the same {@link #getId()} and
* {@link #getOptLockRevision()} and class.
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(this.getClass().isInstance(obj))) {
return false;
}
final AbstractJpaBaseEntity other = (AbstractJpaBaseEntity) obj;
final Long id = getId();
final Long otherId = other.getId();
if (id == null) {
if (otherId != null) {
return false;
}
} else if (!id.equals(otherId)) {
return false;
}
return getOptLockRevision() == other.getOptLockRevision();
}

@Override
public String toString() {
return this.getClass().getSimpleName() + " [id=" + id + "]";
}

@PostPersist
public void postInsert() {
if (this instanceof EventAwareEntity eventAwareEntity) {
doNotify(eventAwareEntity::fireCreateEvent);
}
}

@PostUpdate
public void postUpdate() {
if (this instanceof EventAwareEntity eventAwareEntity) {
doNotify(eventAwareEntity::fireUpdateEvent);
}
}

@PostRemove
public void postDelete() {
if (this instanceof EventAwareEntity eventAwareEntity) {
doNotify(eventAwareEntity::fireDeleteEvent);
}
}

protected static void doNotify(final Runnable runnable) {
// fire events onl AFTER transaction commit
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable);
}

private boolean isController() {
return SecurityContextHolder.getContext().getAuthentication() != null
&& SecurityContextHolder.getContext().getAuthentication()
.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails
&& tenantAwareDetails.isController();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.model;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

import org.eclipse.hawkbit.repository.Identifiable;

/**
* Core information of all entities.
*/
public interface BaseEntity extends Serializable, Identifiable<Long> {

static Long getIdOrNull(final BaseEntity entity) {
return entity == null ? null : entity.getId();
}

/**
* @return user that created the {@link BaseEntity}.
*/
String getCreatedBy();

/**
* @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity} was created.
*/
long getCreatedAt();

/**
* @return user that updated the {@link BaseEntity} last.
*/
String getLastModifiedBy();

/**
* @return time in {@link TimeUnit#MILLISECONDS} when the {@link BaseEntity}
* was last time changed.
*/
long getLastModifiedAt();

/**
* @return version of the {@link BaseEntity}.
*/
int getOptLockRevision();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# hawkBit JPA EclipseLink Vendor integration

Implementation of [EclipseLink](http://www.eclipse.org/eclipselink/) JPA vendor.
Loading

0 comments on commit 00116c7

Please sign in to comment.