Bem, vamos lá.
Já ficou bem melhor. Nada mais a dizer quanto aos pontos 1 e 7.
Quanto ao ponto 2, primeiramente na sua DAOFactory você tem o método getDao(Class<?> daoClass). Observe que agora o parâmetro não é mais utilizado (pois agora o IDAO não é mais genérico), portanto agora você pode eliminar este parâmetro (e isso vai simplificar os seus BOs). Com isso, todas as instâncias dos seus DAOImpl serão iguais, então se você quiser poderá colocá-la em cache, transformar em singleton, ou utilizar o construtor diretamente pois ele se torna um objeto extremamente leve.
Eu nem ia falar do CriteriaBuilder por enquanto. Que bom que você já foi direto nisso.
Quanto as exceções (ponto número 5), vamos ao começo. Primeiramente na classe DAOImpl. Note que lá você captura RuntimeException apenas para relançar como Exception. E se ao invés de fazer isso, você simplesmente não a capturasse e deixasse propagar?
O grande segredo do tratamento de exceções, é saber quem pode tratar a exceção de forma eficiente (usando o catch), e quem não pode (deve relançar ela, ou lançar uma outra exceção tendo a primeira como causa).
Pense bem, uma PersistenceException vinda do JPA não tem como ser tratada no próprio método. Portanto deixe ela se propagar. Só isso já diminui o seu throws Exception para throws PersistenceException. No entanto, se você for subindo as camadas, vai ver que ninguém vai saber tratar essa exceção de forma adequada, o único tratamento possível seria no controller, aonde ele dá um catch (Exception e) ou um catch (Throwable t), gera um log e manda um erro para o usuário. O motivo de não ter aonde tratar o PersistenceException é porque ele de fato não é algo tratável, não há muito o que fazer exceto cuspir um erro na cara do usuário e talvez gerar alguns logs no caminho. Como não é razoável que alguém tente tratar a PersistenceException e ela é uma RuntimeException exatamente por causa disso, então você pode simplesmente apagar a cláusula throws e deixar ela se propagar sem fazer nenhum tratamento especial local, deixando para que o tratador de erro genérico do controller se vire com ela.
Quanto ao ponto 4, nos seus BOs, aplicadas as mudanças que sugeri sobre o ponto 2, você vai ter isso:
dao = new DAOFactoryImpl().getDao();
Qual é o motivo de você não fazer simplesmente isso?dao = new DAOImpl();
Ou então isso?dao = DAOImpl().getInstance();
A menos que você tenha um motivo forte o suficiente, você vai ver que não precisa do DAOFactory.
No entanto, caso queira manter o DAOFactory do jeito que está, sabemos que o DEFAULT_FACTORY_NAME é "JPA". Asssim, isso:
if (DAOFactory.DEFAULT_FACTORY_NAME.equals("JPA"))
dao = (IDAO) this.createNewInstance("br.com.empresa.model.dao.DAOImpl");
É otimizado pela JVM nisso:
if (true)
dao = (IDAO) this.createNewInstance("br.com.empresa.model.dao.DAOImpl");
Que por sua vez é novamente otimizado para isso:
Vamos supor que ao invés do nome da classe, o createNewInstance recebesse uma referência da própria classe (o que já eliminaria o método getClass(String)). Como a classe DAOImpl está dentro do seu JAR, então é seguro referenciá-la diretamente. Você teria isso:
Que efetivamente é a mesma coisa que isso:
E assim o seu método se tornaria isso:
public IDAO getDao() {
IDAO dao = null;
try {
dao = new DAOImpl();
} catch (Exception e) {
e.printStackTrace();
}
return dao;
}
Não há como uma exceção ser lançada ali, então podemos eliminar o try e o catch. Logo, temos isso:
public IDAO getDao() {
return new DAOImpl();
}
E então, em todos os lugares aonde você tem isso:
Você poderia substituir por isso:
O que elimina a sua DAOFactory. Por sinal, como DAOImpl é a única implementação de IDAO, e interfaces não tem muito sentido se não forem para permitir múltiplas implementações, você poderia eliminar a IDAO também e transformar a sua DAOImpl apenas em SimpleDAO.
No entanto, caso queira manter o DAOFactory (por exeplo, você pretende fazer com que aquele if no começo possa variar), então voltando ao ponto 5, que tipo de tratamento você pode fazer caso não seja possível criar-se o DAO? Resposta: Nenhum. Qual é a razão de não ser possível criar-se o DAO? Resposta: Existe uma classe essencial faltando na aplicação. Portanto, neste caso aí, pode criar uma classe ConfigurationError, ApplicationAssemblyError ou algo parecido (observe que é Error, e não Exception). Então encapsule qualquer exceção capturada nestes pontos como causa deste seu Error, dê um throw no seu Error e esqueça. Esse é um exemplo típico de onde um Error é mais apropriado do que uma Exception simplesmente porque ele indica que há um problema fatal e irrecuperável na sua aplicação: Um pedaço dela está faltando ou no mínimo o arquivo properties dela (que você já eliminou) está faltando ou está inválido. Repetindo isso é só para o caso de você querer manter o DAOFactory, pois caso queira eliminá-lo, nada disso vai precisar existir. E caso queira eliminá-lo, é uma coisa a menos para complicar a arquitetura e uma coisa a menos para debuggar.
Bem, este artigo descreve bem o que é (na minha polêmica opinião) essa ideia de se ter DAOFactory:
http://www.marcoscintra.org/fabula_porcos.asp
Quanto ao ponto 3, o que é que o AbstractEntity te dá de bom? Pense bem antes de responder a esta pergunta, qual é o grande motivo de você ter esta classe, o que ela trás de bom? Na minha opinião, ela apenas economiza um pouco de trabalho de digitação nas subclasses. No entanto, acredito que você já deve ter visto sobre os problemas que herança te dá: A herança é um tipo bem forte de acoplamento. Trocar um pouco de trabalho de digitação por um tipo forte de acoplamento normalmente não é boa ideia. Quanto a questão da chave primária, embora seja desejável e seja boa ideia que elas sempre sejam ids simples autoincrementados, há algumas situações aonde isso não é desejado. Um exemplo de uma dessas situações é quando você tem uma tabela de relacionamento N-N entre duas outras tabelas e depois você decide acrescentar mais um atributo pertinente ao relacionamento, o que te força a transformá-la em outra tabela, no entanto você quer que a PK continue sendo as duas FKs das tabelas relacionadas.
Quanto ao ponto 8, O JUnit é uma ferramenta bem facinha de usar e requer zero de configuração. O Log4j é mais chatinho, mas recomendo usar o log4j por trás do slf4j. Dê uma pesquisada neles, inclua os que você quer no projeto, e daí voltamos aqui ao tópico.
Quanto ao ponto 6, deixo para o próximo post.