#833: AddressbookDao.kt

projectforge-business/src/main/kotlin/org/projectforge/business/address/AddressbookDao.kt Type: Data Access Object (DAO) — Spring-managed service
Purpose: Persistence and access-control layer for address book entities
Location: projectforge-business/src/main/kotlin/org/projectforge/business/address/AddressbookDao.kt 174 lines · 120 code · 38 comments · 16 blank
Summary: AddressbookDao is the primary data-access object for AddressbookDO entities. It extends BaseDao to inherit standard CRUD operations (save, update, delete, find, getList, markAsDeleted, undelete) and overrides select() to enforce per-user access-control filtering via AddressbookRight. It is annotated as a Spring @Service singleton and is one of the oldest files in the codebase, present since the initial commit.

Architecture & Position in the System

Class Hierarchy

AddressbookDao extends BaseDao<AddressbookDO>, which itself is a generic Hibernate DAO providing:

Access-Control Integration

The DAO participates in ProjectForge's rights-management system in two ways:

MechanismDescription
UserRightId.MISC_ADDRESSBOOK Set in the init block. This right is checked by BaseDao before any mutating operation (save, update, delete, etc.) to ensure the caller holds the MISC_ADDRESSBOOK permission.
AddressbookRight Used inside the overridden select() method. Builds an HQL query that joins onto the user/group rights tables and restricts results to address books the current user can read, based on the ownership and group-access rules inherited from BaseUserGroupRightsDO.

Dependencies

DependencyRole
UserDaoAutowired; used by setOwner() to resolve a PFUserDO by its numeric ID before assigning it as the owner of an address book.
HistoryFormatUtilsAutowired; provides formatting helpers for audit-log (history) entries, likely used via BaseDao when recording changes.

Key Methods

setOwner(ab: AddressbookDO, userId: Long)

Resolves a PFUserDO via UserDao and assigns it to the address book's owner field. This is a convenience method that centralises the owner-lookup pattern used by UI controllers.

override fun select(filter: BaseSearchFilter): List<AddressbookDO>

Overrides the base implementation to cast the generic filter into an AddressbookFilter and incorporate AddressbookRight-based filtering. The generated HQL adds JOIN clauses on the rights tables so that only address books visible to the current user (by ownership, group membership, or explicit access grants) are returned. Users without MISC_ADDRESSBOOK may see an empty list.

override fun newInstance(): AddressbookDO

Factory method required by the generic DAO infrastructure; returns a plain new AddressbookDO().

override val additionalSearchFields: Array<String>

Returns ADDITIONAL_SEARCH_FIELDS, a constant array listing the entity properties that should be included in full-text / Hibernate Search queries beyond the default fields inherited from BaseDO.

Git History

CommitDescription
868d6abb7Copyright year bump: 2025 → 2026
63081666fSource file headers: 2024 → 2025
4c04cfd65MAJOR: Migration of integer IDs to Long IDs (changed all Integer-based keys to Long)
b6092df09Copyright 2023 → 2024
ab45d51faCopyright 2001-2022 → 2001-2023
5f7ef41b8Copyright 2021 → 2022
ceb63e8a1Source code header: (C) 2001-2021
7c79f1922Copyright of source header → 2020
dd5ca38acCopyRight of all java file-headers updated or created
2d86a8cd6PROJECTFORGE-2306: Refactorings for keeping access check at update time (ensured UserRightId checks are honoured on every write path)
9ebb88522Initial commit — one of the original files from the project's founding import

Design Rationale

Why a separate DAO class?

ProjectForge follows the standard DAO-per-entity pattern. Each business entity gets its own DAO so that cross-cutting concerns (access control, auditing, full-text indexing) can be customised per entity. The generic BaseDao handles boilerplate CRUD and the per-entity subclass injects entity-specific rules — here the address-book access logic.

Why override select() rather than use Spring Security annotations?

ProjectForge predates Spring Security's widespread adoption and uses its own rights-management layer built on HQL JOINs. Overriding select() allows the access checks to be compiled directly into the database query, avoiding the N+1 problem that would arise from loading all rows and then filtering in application code. This approach also lets the filter combine access-control predicates with user-supplied search criteria in a single HQL statement.

Why is an AddressbookRight class used instead of inline HQL?

The AddressbookRight class encapsulates the user/group permission model defined by BaseUserGroupRightsDO. By delegating to a separate class, the same access logic can be reused in reporting, export, and REST endpoints without duplicating the multi-JOIN pattern. It also makes the access model easier to test in isolation.

Why @Service instead of @Repository?

While Spring's @Repository stereotype is typical for DAOs, ProjectForge uses @Service throughout its DAO layer. This likely reflects a convention established before @Repository's exception-translation feature was widely adopted, or a deliberate choice to treat DAOs as transactional service components. The functional difference is negligible since both are Spring-managed singletons.

Longevity

This file has been in the codebase since the initial commit (9ebb88522) and has survived every major refactoring — the integer-to-Long migration, multiple copyright updates, and the access-check refactoring of PROJECTFORGE-2306 — attesting to the stability of the DAO abstraction.