Problema com ID personalizado - Hibernate

Pessoal, criei um ID personalizado pra minha entidade, mas o seguinte erro é lançado ao tentar inserir pelo JPA:
org.hibernate.PersistentObjectException: detached entity passed to persist: br.unb.pessoal.model.sipes.DadosFuncionais

Segue declarações:
Entidade:

public class DadosFuncionais implements Serializable {

    private static final long serialVersionUID = -288176893648980290L;

    @Id
    @GenericGenerator(name = "gerador_matricula", strategy = "br.unb.pessoal.infra.sipes.DadosFuncionaisIDGenerator")
    @GeneratedValue(generator = "gerador_matricula")
    @Column(name = "MatSipes", unique = true, nullable = false, insertable = true)
    private Integer matSipes;

Gerador:

public class DadosFuncionaisIDGenerator implements IdentifierGenerator {

    @Override
    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
        return this.gerarMatSipes();
    }

    /**
     * Gera a nova matrícula FUB para o servidor.
     *
     * @return Nova Matrícula FUB.
     */
    private Integer gerarMatSipes() {
        Integer ultimaMatricula = PessoalInfra.getInstance().getDadosFuncionaisRepository().getUltimaMatricula();
        int matTemp = 0;
        if (ultimaMatricula != null) {
            matTemp = ((ultimaMatricula / 10) + 1);
        }
        return Integer.parseInt(this.calcularNovaMatricula(matTemp));
    }

    /**
     * Realiza o cálculo da nova Matrícula FUB. Este método reflete a 'Function'
     * CDC da 'FubLibs' implementada no VB. <br/>
     * <p>
     * Recebe como parâmetro a última matrícula armazenda no banco de dados,
     * dividida por 10 e somada uma unidade (+1). Por receber um inteiro, o
     * valor passado como parâmetro é arredondado para baixo antes da
     * soma.</p><br/>
     *
     * @param ultimaMatricula Última matrícula, dividida por 10, arredondada
     * para baixo e somada em uma unidade (+1).
     * @return Nova matrícula FUB preenchida com zeros à esquerda, caso
     * necessário.
     */
    private String calcularNovaMatricula(int ultimaMatricula) {
        String auxiliar = String.valueOf(ultimaMatricula).trim();
        int qtd = auxiliar.length();
        int fim = qtd;
        int nro = 0;

        for (int i = 1; i < fim; i++) {
            nro = nro + (qtd + 1) * Integer.parseInt(auxiliar.substring(i, i + 1));
            qtd--;
        }

        Double novaMatricula = (11 - (nro % 11)) % 10 + (Double.valueOf(auxiliar) * 10);
        return UtilitariosUnB.aplicarZerosAEsquerda(String.valueOf(novaMatricula.intValue()), fim + 1);
    }

}

Método da persistência:

public DadosFuncionais inserir(DadosFuncionais df) {
try {
    if (df != null) {
        this.getEntityManager().persist(df);
        this.getEntityManager().flush();
        this.getEntityManager().clear();
        
        DadosFuncionais dadosFuncionais = this.findByCodigoPessoa(df.getPesCodigoPessoa());
        if (dadosFuncionais != null) {
            return dadosFuncionais;
        }
    }
} catch (Exception e) {
    throw e;
}
return null;
}

Sei que se eu adicionar um auto-incremento no banco esse problema seria resolvido, mas atualmente não é possível criar um campo chave auto-incremento no banco de dados, pois trata-se de uma aplicação para substituir um sistema legado (em VB) que ainda está em uso, estamos migrando alguns módulos, estou tentando replicar o código no Java, mas esbarrei nesse problema (dentre outros).

Desde já agradeço a ajuda!

Nao usa generator e seta esse matSipes manualmente antes de inserir o registro no banco.

@Id
@Column(name = "MatSipes")
private Integer matSipes;

Se houver apenas uma pessoa trabalhando com o sistema isso funciona, mas no caso de acessos concorrentes, o sistema gera uma violação de chave primária, visto que o ID é gerado baseando-se no último cadastrado, por isso a necessidade de criar o ID no momento da inclusão.

A dica foi dada com base no que você mesmo fez, que já não é garantido em caso de concorrência, por estar tratando via aplicação/hibernate e não diretamente via SGDB. A diferença que voce vai chamar o gerarMatSipes() programaticamente.

Pra garantir acesso concorrente só mesmo usando campo autoincremento ou sequence (de acordo com o que seu banco suportar.

@javaflex estou analisando a possibilidade de alterar o BD para criar um auto-incremento no na tabela, pois até o momento, não sei o impacto que isso pode causar no “legado”…
Mas o problema que relatei é que o EntityManager reclama com esse erro (detached entity passed to persist) até onde eu sei, isso deveria ocorrer ao tentar atualizar um registro, eu estou tentando inserir, e, mesmo passando o campo preenchido, a mensagem continua.

Cara, impacto e testes já vai ter por mudar de tecnologia, entao ou migra tudo ou deixa em VB mesmo se estiver preocupado com impactos.

Eu estava verificando com o pessoal aqui, e me informaram que esse campo possui um dígito verificador, por esse motivo é que a aplicação gera o ID.
Vou pensar em outra solução, visto que exitem outras regras e novas funcionalidades envolvidas.
Posteriormente posto aqui a solução…

Agradeço a ajuda!

Deixa a chave primária como auto incremento ou sequence. O campo “MatSipes” deixa como um campo normal, adiconando uma constraint unique key só pra garantir. Sei que ta pegando um legado, mas um campo significativo pro negócio nao deveria ficar como chave primária.

Você também pode fazer igual no VB, tudo via SQL diretamente, sem essas complicações de configuracoes de Hibernate. JdbcTemplate é bem mais leve e sob controle.

Apenas para explicar o contexto, a MatSipes é um ID numérico único maior que 0 gerado pelo sistema contendo um dígito verificador. O sistema legado gerava esse ID e armazenava em um campo indexado no banco de dados. Visando evitar qualquer tipo de impacto no legado (que ainda está em produção), escolhemos por não alterar as tabelas no banco de dados ao reescrever o sistema em Java. Da mesma forma como ocorria no legado, reescrevi o código em Java, porém, ao tentar inserir no banco o Hibernate lançava a exceção informada. Não entendi o motivo, visto que o erro apresentado eu só havia visto ao tentar atualizar um registro.

Então, pra resolver essa questão, transcrevi o código que gera o ID em uma função T-SQL armazenada no banco de dados, estou realizando a persistência executando uma query nativa do SQL Server no Java chamando a função para preenchimento do referido campo. O erro não voltou a ocorrer e após alguns testes, não ocorreram problemas de concorrência.

Mais uma vez agradeço as sugestões.