3

I've the following definition for two different datasources in the application.yml of a Micronaut application -

datasources:
  default:
    url: <jdbc-url>
    driverClassName: org.postgresql.Driver
    username: <username>
    password: <password>
    dialect: POSTGRES
    schema: <schema>
  ms-sql:
    url: <jdbc-url>
    driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
    username: <username>
    password: <password>
    dialect: org.hibernate.dialect.SQLServer2012Dialect
    schema: <schema>

How could i get a specific instance of EntityMananger injected in my Service class below?

Service Class Snippet:

@Inject
private EntityManager em;

@Transactional
@Override
public int callSP(int num) {
    StoredProcedureQuery query = em.createStoredProcedureQuery("storelist.testSP1")
                                    .registerStoredProcedureParameter("InputInt", Integer.class, ParameterMode.IN)
                                    .registerStoredProcedureParameter("OutputInt", Integer.class, ParameterMode.OUT)
                                    .setParameter("InputInt", num);
    query.execute();
    Integer result = (Integer) query.getOutputParameterValue("OutputInt");
    
    return result;
}
V.Vidyasagar
  • 633
  • 6
  • 13

1 Answers1

2

For each DataSource configuration (also injected into a bean DataSourceConfiguration driven by ConfigurationProperties), Micronaut will create a qualified SessionFactory bean.

Underneath, each qualified SessionFactory will lead to a TransactionManager qualified bean creation.

As per the io.micronaut.context.annotation.EachBean injection mechanism, each bean created will be @Named after the bean qualifying name that drove its creation.

The short injection path, from configuration to bean creation, then would be as follows:

datasources.some-datasource-name (configuration item within your .yaml, .properties...)
              |
              |
              v
    DataSourceConfiguration @Named("some-datasource-name")
              |
              |
              v
         DataSource @Named("some-datasource-name")
              |
              |
              v
        SessionFactory @Named("some-datasource-name")
              |
              |
              v
      TransactionManager @Named("some-datasource-name")
              |
              |
              v
     RepositoryOperations @Named("some-datasource-name")

You will have then either to use the qualified TransactionManager programmatically along with a transaction-aware EntityManager to perform JPA operations (note that the @Transactional annotation is no longer needed in a programmatic transactional style):

@Singleton
public class MyServiceImpl implements MyService {

    private EntityManager em; // transaction-aware bean

    private final SynchronousTransactionManager<Connection> transactionManager;

    public MyServiceImpl(@NonNull EntityManager entityManager, @NonNull @Named("ms-sql") SynchronousTransactionManager<Connection> transactionManager) {
        this.em = entityManager;
        this.transactionManager = transactionManager;
    }

    @Override
    public int callSP(int num) {
        return transactionManager.executeRead(status -> {
            StoredProcedureQuery query = em.createStoredProcedureQuery("storelist.testSP1")
                    .registerStoredProcedureParameter("InputInt", Integer.class, ParameterMode.IN)
                    .registerStoredProcedureParameter("OutputInt", Integer.class, ParameterMode.OUT)
                    .setParameter("InputInt", num);
            query.execute();    
            return (Integer) query.getOutputParameterValue("OutputInt");
        });
    }
}

Or inject a qualified RepositoryOperations, that would be an instance of HibernateJpaOperations in your case and use it for querying your data source.

tmarwen
  • 15,750
  • 5
  • 43
  • 62