Injeção de controlador de transações não satisfeita no Spring MVC 4 + JPA 2 e Hibernate 5

Boa noite pessoas.

Depois de longos anos fora do mundo da programação Java, voltei a estudar com foco em um projeto de desenvolvimento web para o futuro.

Pois bem, estou tentando utilizar o Spring MVC 4, com JPA 2 e Hibernate 5 para controlar as transações com o banco de dados MySQL 8.

As conexões são todas realizadas, realizo consultas normalmente. O problema começa quando tento persistir, remover ou atualizar algum objeto, onde é necessário o controle de transação, e o Spring parece não estar fazendo essa injeção de dependência do EntityManager pra mim.

Segue os códigos:

dispatcher-servlet.xml


<!-- CONFIGURAÇÃO PARA ACEITAR ANOTAÇÕES -->
<mvc:annotation-driven />
<context:component-scan base-package="base" />

<!-- CONFIGURAÇÃO DE BEAN PARA O INTERCEPTOR DE LOGIN -->
<!--<mvc:interceptors>
    <bean class="base.interceptors.LoginInterceptor" />
</mvc:interceptors>-->

<!-- CONFIGURAÇÃO DE BEAN PARA RESOLUÇÃO DAS VIEWS -->
<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      p:prefix="/WEB-INF/jsp/"
      p:suffix=".jsp" />

applicationContext.xml


<!-- ATIVANDO AS TRANSAÇÕES VIA ANOTAÇÃO PELOS CONTROLLERS -->
<tx:annotation-driven transaction-manager="txManager" />

<bean id="mySQLDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/teste?useSSL=false&amp;serverTimezone=UTC"></property>
    <property name="username" value="root"></property>
    <property name="password" value="senha"></property>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="mySQLDataSource" />
<property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Classe Pessoa

package base.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "pessoas")


public class Pessoa {
@Id
@GeneratedValue
@Column(name = "id_pessoa", nullable = false)
private long id;

@Column(name = "nome_pessoa", nullable = false)
private String nome;

public Pessoa() {
}

public Pessoa(String nome) {
    this.nome = nome;
}

public Pessoa(long id, String nome) {
    this.id = id;
    this.nome = nome;
}



public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getNome() {
    return nome;
}

public void setNome(String nome) {
    this.nome = nome;
}
}

Classe JPAPessoaDAO

package base.hibernate;

import base.dao.interfaces.PessoaDAO;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import base.models.Pessoa;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceProperty;
import org.springframework.stereotype.Repository;

/*
    A ANOTAÇÃO @REPOSITORY É UTILIZADA PARA A RESOLUÇÃO DE DEPENDÊNCIA DO DAO
*/
@Repository
public class JPAPessoaDAO implements PessoaDAO {

/*
    Um PersistenceContext (Contexto Persistente) é um local onde ficam armazenados os objetos
    (entidades) que estão sendo manipulados pelo EntityManager corrente
*/
@PersistenceContext
EntityManager manager;

@Override
public void inserirPessoa(Pessoa pessoa) {
    manager.persist(pessoa);
}

@Override
public Pessoa buscarPessoa(long id) {
    Pessoa pessoa = manager.find(Pessoa.class, id);

    return pessoa;
}

@Override
public void removerPessoa(long id) {
    Pessoa pessoa = manager.find(Pessoa.class, id);
    manager.remove(pessoa);
}

@Override
public void atualizarPessoa(Pessoa pessoa) {
    manager.merge(pessoa);
}

@Override
public List<Pessoa> listar() {
    List<Pessoa> listaPessoas = manager
            .createQuery("select p from Pessoa as p")
            .getResultList();

    return listaPessoas;
}

@Override
public List<Pessoa> listarPorID(long id) {
    Query query = manager
            .createQuery("select p from Pessoa as p where p.id = : paramID");
    query.setParameter("paramID", id);

    List<Pessoa> listaPessoas = query.getResultList();

    return listaPessoas;
}

@Override
public EntityManager getEntityManager() {
    return manager;
}

/*public static void main(String[] args) {

    //JPAPessoaDAO pessoaDAO = new JPAPessoaDAO();

    // INSERT
    //Pessoa pessoa = new Pessoa("VALMORPE");
    //pessoaDAO.inserirPessoa(pessoa);

    // SELECT
    //Pessoa pessoa = pessoaDAO.buscarPessoa(4L);
    //System.out.println("Pessoa de ID 4: " + pessoa.getNome());

    // DELETE
    //pessoaDAO.removerPessoa(6);

    // UPDATE
    //Pessoa pessoa = new Pessoa(5L, "VALMIR");
    //pessoaDAO.atualizarPessoa(pessoa);

    // SELECT COM QUERY PRÓPRIA E WHERE RETORNANDO LISTA
    //List<Pessoa> listaPessoas = pessoaDAO.listar();
    //for (Pessoa pessoa : listaPessoas) {
        //System.out.println("Pessoa de ID " + pessoa.getId() + " e nome " + pessoa.getNome());
    //}
    //

    // SELECT COM QUERY PRÓPRIA, WHERE E RETORNANDO LISTA COM PARAMETRO
    //List<Pessoa> listaPessoas = pessoaDAO.listarPorID(5L);
    //for (Pessoa pessoa : listaPessoas) {
        //System.out.println("Pessoa de ID " + pessoa.getId() + " e nome " + pessoa.getNome());
    //}
}*/
}

Classe PessoaController

package base.controllers;

import base.dao.interfaces.PessoaDAO;
import base.models.Pessoa;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;

import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/*
  A ANOTAÇÃO @TRANSACTIONAL INFORMA  
*/

@Controller
public class PessoaController {

@Autowired
PessoaDAO pessoaDAO;

//Logger log = Logger.getLogger(PessoaDAO.class.getName());

@RequestMapping("lista-pessoas")
public String listarPessoas(Model model) throws InvocationTargetException {

    //DebugUtils.transactionRequired("PessoaController.listarPessoas");

    List<Pessoa> listaPessoas = pessoaDAO.listar();
    model.addAttribute("listaPessoas", listaPessoas);

    return "lista-pessoas";
}

@Transactional
@RequestMapping("add") 
public String add(Model model) {

    Pessoa pessoa = new Pessoa ("PESSOA TESTE");
    pessoaDAO.inserirPessoa(pessoa);

    //model.addAttribute("info", pessoaDAO.getEntityManager().isOpen());
    model.addAttribute("info", 
pessoaDAO.getEntityManager().isJoinedToTransaction());

    return "add";
}
}

persistence.xml


<persistence-unit name="pessoas" transaction-type="RESOURCE_LOCAL">
        <class>base.models.Pessoa</class>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
  <property name="hibernate.show_sql" value="true"/>
  <property name="hibernate.format_sql" value="true"/>
  <property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>

( repetido )