I am developing a web application, where, among other things, I need to upload a file to a BLOB column in a mysql table. From what I can see this can be done with JDBC calls (PrepareStatement() etc), but I would like to be able to do this in an EJB class - what I have cobbled together looks like this:
#Stateless
public class ItemsSession {
#PersistenceContext(unitName ="officePU")
private EntityManager em;
private List<Items> itl;
private static final Logger logger=
Logger.getLogger(ItemsSession.class.getName());
...
public String updateDocument(Integer id,InputStream is) throws SQLException{
String msg="";
try{
java.sql.Connection conn = em.unwrap(java.sql.Connection.class);
PreparedStatement pstmt=conn.prepareStatement("UPDATE Documents SET doc = ? WHERE id = ?");
pstmt.setBinaryStream(1, is);
pstmt.setLong(2, id);
pstmt.executeUpdate();
pstmt.close();
}
catch (PersistenceException e){
msg=e.getMessage();
}
return msg;
}
...
}
I have two questions, though:
I would like not to use JDBC directly - is there a way to do this that is 'pure JPA' (edit: not EJB)?
If I have to do it this way, is the PreparedStatement included in the container managed transaction?
Another edit: the code above does the job - I have now tested it. But it isn't pretty, I think.
The first thing you have to do to persist BLOB values the JPA way is you define an entity. The following an example pseodo code:
#Entity
public class Documents {
#Id
private Long id;
#Lob
private byte[] doc;
// .... getters + setters
}
Then you modify your EJB as follows:
#Stateless
public class ItemsSession {
#PersistenceContext(unitName ="officePU")
private EntityManager em;
// ... the rest of your code
public String updateDocument(Integer id,InputStream is) throws SQLException{
String msg = "";
Documents docs = em.find(Documents.class, id); // fetch record from DB
// Assuming your InputStream is a ByteArrayInputStream
byte[] doc = new byte[is.available()]; // create target array
is.read(doc, 0, doc.length); // read bytes into byte array
docs.setDoc(doc); //
return msg; // returning exception message from method call?????
}
...
}
If you don't change the defaults EJB methods are invoked in a transaction by default. So when your method exits, the update should be synchronized with the database.
This answer kann only help you if you read and understand the basics of the JPA. And here is an official tutorial to JPA persistence among other lots of tutorials on the web.
Update
I would like not to use JDBC directly - is there a way to do this that is 'pure JPA'
No.
If I have to do it this way, is the PreparedStatement included in the container managed transaction?
No. But you can use bean managed transaction. If you want to use BMT, the following pseudocode might help you:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class ItemsSession {
#Resource UserTransaction ut;
#Resource DataSource datasource; // you should define datasource on your application server
...
public String updateDocument(Integer id,InputStream is) throws SQLException{
// ...
try (java.sql.Connection conn = datasource.getConnection();
PreparedStatement pstmt=conn.prepareStatement("UPDATE Documents SET doc = ? WHERE id = ?")) {
pstmt.setBinaryStream(1, is);
pstmt.setLong(2, id);
ut.begin();
pstmt.executeUpdate();
ut.commit();
} catch (PersistenceException e){
// ... error handling
}
return ...;
}
...
}
I think you use EJB intergate to with JPA , because you are using this:
#PersistenceContext(unitName ="officePU")
Refernce: http://www.adam-bien.com/roller/abien/entry/ejb_3_persistence_jpa_for
Related
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I'm trying to create a Java application that will run on a hypothetical client machine, where members of staff can both view or add customer details from a local MySQL database.
I'm trying to use JPA to do so, with query methods being in this form:
public class DataManagerImpl implements DataManager{
#PersistenceContext
private EntityManager em;
public List<Customer> AllCustomers(){
TypedQuery<Customer> query = em.createNamedQuery("Customer.findAll", Customer.class);
return query.getResultList();
} }
I've got a DBConnection class:
public class MyDBConn implements DBConnectivity {
#Resource(mappedName="jdbc:mysql://localhost:3306/solsoft_DB") DataSource dataSource;
Connection myConn = null;
public Connection open_Connection() {
String user = "root";
String pass = "password";
try {
Class.forName("com.mysql.jdbc.Driver");
myConn = dataSource.getConnection(user, pass);
return myConn;
} catch (Exception exc) {
exc.printStackTrace();
return myConn;
}
}}
And then in my main method:
DataManagerImpl dm = new DataManagerImpl();
List<Customer> allCustomers = dm.AllCustomers();
for(Customer c : allCustomers){
String cust = "" + c.getForename() + " " + c.getSurname();
System.out.println(cust);
}
I'd really appreciate if anyone could point my in the right direction on how to actually go about getting some information from the DB using JPA in this way.
The application will be running in a server? What server?
Or is an standalone application?
My guess (the best I can do as there are many things not pointed in the question) is that you are trying to supply the connection that PersistenceContext should use.
If its like this and you are using JPA you should register an EntityManagerFactory with the required properties for connection and get your PersistenceContext from that factory. (See an example here)
Another way to go would be to edit your persistence.xml file defining this properties inside the file like this and just let your context handle the logic for database connection.
I'm using Spring JdbcTemplate on one of my projects and now, when there are really very much requests with it - I started to face this exception:
org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback;
uncategorized SQLException for SQL [{? = call API.get_data_for_nb(?, ?)}];
SQL state [99999]; error code [17009]; Closed Statement;
nested exception is java.sql.SQLException: Closed Statement
So the Closed Statement exception is received when you try to execute statement that is already closed, but in my case I don't close it by myself - I use JdbcTemplate exactly for that. So, firstly, what could be the reason for that?
The JdbcTemplate object itself is contained in #Stateless EJB in this way:
#Stateless(name = "NbEdwServiceEJB")
public class NbEdwServiceBean implements NbEdwServiceLocal, NbEdwServiceRemote {
#Resource(mappedName = JNDI)
private DataSource dataSource;
private static volatile JdbcTemplate jdbcTemplate;
#PostConstruct
protected void construct() {
synchronized (NbEdwServiceBean.class) {
if (jdbcTemplate == null) {
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
}
}
}
private String getDataFromDB(final String request, final int isDigitalSignVerified) {
String response = null;
try {
response = jdbcTemplate.execute(SQL_GET_DATA, new CallableStatementCallback<String>() {
public String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.registerOutParameter(1, Types.VARCHAR);
cs.setInt(2, isDigitalSignVerified);
cs.setString(3, request);
cs.executeUpdate();
return cs.getString(1);
}
});
} catch (DataAccessException ex) {
LOGGER.error("getDataFromDB()", ex);
}
return response;
}
}
I know that this is maybe not the strictly right way to do it, I could just create instance of JdbcTemplate for every stateless bean - so I might do just that. So, secondly, why is this ever happening? My suppose was that JdbcTemplate's execute method isn't thread safe, but can someone give the full explanation on what is going on?
I have JEE version 5 running on WebLogic 10.3.5 if it's matter.
#Tolegen Izbassar I'm sorry that you're stuck with EE5.
Concerning the Singleton and EE5 there were some alternatives out there. One is vendor specific extensions, for example JBoss 5.x had service beans providing Singleton+JMX. A second solution is to use a earlier version of Jboss Seam compatible with EE5. A third alternative is to use the ServerContext from the Servlet API.
What you're trying to do in #PostConstuct is definitely not good. Non final statics in SLSB is a no go.
I suggest to have a look, at section 29.3 from Spring framework reference which describes EJB - Spring integration, an example from that section:
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
public class MyFacadeEJB implements MyFacadeLocal {
// automatically injected with a matching Spring bean
#Autowired
private MyComponent myComp;
// for business method, delegate to POJO service impl.
public String myFacadeMethod(...) {
return myComp.myMethod(...);
}
This is my connection detail in JBoss standalone.xml
<connection-url>
jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=xx.1xx.119.1xx)(PORT=1521))(LOAD_BALANCE=on)(FAILOVER=on))(CONNECT_DATA=(SERVICE_NAME=XE)))
</connection-url>
I want to handle a corner case of failover where post getting EntityManager object during a call of persist(), the connection is lost. Failover option is not switching to next database in the same transaction, it switches to active connection in the next transaction. I attempted something like this: (Catch Exception and get updated bean object)
public EntityManager getEntityManager() {
try {
entityManager = getEntityManagerDao(Constant.JNDI_NFVD_ASSURANCE_ENTITY_MANAGER);
} catch (NamingException e) {
LOGGER.severe("Data could not be persisted.");
throw new PersistenceException();
}
return entityManager.getEntityManager();
}
/**
* Inserts record in database. In case multiple connections/databases exist, one more attempt will be made to
* insert record.
*
* #param entry
*/
public void persist(Object entry) {
try {
getEntityManager().persist(entry);
} catch (PersistenceException pe) {
LOGGER.info("Could not persist data. Trying new DB connection.");
getEntityManager().persist(entry);
}
}
private static Object getJNDIObject(String path) throws NamingException {
Object jndiObject = null;
InitialContext initialContext = new InitialContext();
jndiObject = initialContext.lookup(path);
return jndiObject;
}
private static AssuranceEntityManager getEntityManagerDao(String path) throws NamingException {
return (AssuranceEntityManager) getJNDIObject(path);
}
But this one also is not helping. After catching the exception, getting a new bean with JNDI lookup does not contain an updated new connection and an exception is thrown. This results in loss of data of that transaction.
Please suggest how to handle this corner case of "Connection lost post getting EntityManager and before persisting."
I think it's quite impossible what you want to achieve. The thing is that if internal DB transction is aborted then the JTA transaction is in abort state and you can't continue with it.
I expect it's kind of similar to this case
#Stateless
public class TableCreator {
#Resource
DataSource datasource;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void create() {
try(Connection connection = datasource.getConnection()) {
Statement st = connection.createStatement();
st.execute("CREATE TABLE user (id INTEGER NOT NULL, name VARCHAR(255))");
} catch (SQLException sqle) {
// ignore this as table already exists
}
}
}
#Stateless
public class Inserter {
#EJB
private TableCreator creator;
public void call() {
creator.create();
UserEntity entity = new UserEntity(1, "EAP QE");
em.persist(entity);
}
}
In case that table user exists and you would use annotation #TransactionAttribute(TransactionAttributeType.REQUIRED) then the create call will be part of the same jta global transaction as call of persist. As in such case the transaction was aborted the persist call would fail with exception like (postgresql case)
Caused by: org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block
I mean if Oracle jdbc driver is not able to to handle connection fail transparently to JBoss app server and throws the exception upwards then I think that the only possible solution is to repeat the whole update action.
Just a background since I am used to using JDBC since I worked on an old project.
When saving into database, I always set the value to -1 for any unsuccessful insert
public class StudentDao{
//This dao method returns -1 if unsuccessful in saving
public int save(Student stud){
Statement st = con.createStatement();
try{
int val = st.executeUpdate("INSERT student VALUES(.......)");
}catch(Exception e){
return -1;
}
}
}
Based on the return I could could tell if the insert is successful so that I could do the exact logic.
(Tell the user that the transaction is incomplete...)
Now, I used EJB in persisting entity. Most of the tutorials that I am seeing only have this construct.
Netbeans is generating this code also with a 'void' return.
#Stateless
public class StudentFacade{
#PersistenceContext(unitName = "MyDBPU")
private EntityManager em;
public void save(Student student){
em.persist(student);
}
}
When saving entity on a servlet, it just call the method like this code.
#WebServlet(name = "StudentServlet",
loadOnStartup = 1,
urlPatterns = {
"/addStudent",})
public class StudentServlet extends HttpServlet {
#EJB
private StudentFacade studentFacade;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//extract HTTP form request parameters then set
Student stud = Util.getStudent(request);
studentFacade.save(stud);
}
}
But how will I know if the insert is successful? (Dont catch the exception and then just let it propagate.
I have configured my error page so obviously this would catch the error???)
Sorry I am getting confused on integrating my EJB components but I am seeing its benefits.
I just need some advise on some items. Thanks.
The container will propagate the exception to the caller (if you don't do anything with it inside the EJB). That would be probably the SQLException I guess. You can catch it on the servlet and do whatever you want with it. If you use Container Managed Transactions (CMT) the transaction will be rolled back for you automatically by the container and the student object won't be added. As you said, you can of course leave the exception on the web layer as well and then prepare a special error page for it. All depends on your usage scenario.
I m using glassfish v2 and persistence in a web application.
I m calling persistence codes using a normal java class file inside a web Application
I can select easily using this code: -
#PersistenceUnit
public EntityManagerFactory emf;
EntityManager em;
public List fname (String id) {
String fname = null;
List persons = null;
//private PersistenceManagerFactory persistenceManagerFactory;
try {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
em = emf.createEntityManager();
persons = em.createQuery("select r from Roleuser r").getResultList();
int i=0;
for (i=0;i<persons.size(); i++)
System.out.println("Testing n "+ i +" " + persons.get(i));
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
I want to update using JTA as the persistence.xml file has
transaction-type="JTA"
When i try using update using this code i get a nullPointerException without any traces in the log
#PersistenceUnit
public EntityManagerFactory emf;
EntityManager em;
Context context;
#Resource
private UserTransaction utx;
public List fname (String id) {
String fname = null;
List persons = null;
try {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
utx.begin();
em = emf.createEntityManager();
int m = em.createQuery("update Roleuser r set r.firstName = 'Jignesh I' where r.userID=9").executeUpdate();
utx.commit();
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
Any help
Thanks
Pradyut
Perhaps your bean isn't managed - i.e. it's not part of any context (spring, EJB). How are you creating your object?
You really should not call createEntityManager() - inject one using #PersistenceContext
You must be absolutely sure you need JTA before using it.
You seem to be using PersistenceUnit, but then re-assign the etm - I'd suggest drop both and see p2 above.
If you are not using any dependecy injection at all, then drop all the annotations, retain the current code, and type:
em.getTransaction().begin();
...
em.getTransaction().commit();
(and define RESOURCE_LOCAL in your persistence.xml. You really don't need JTA)
well the code should be without any nightmares...(atleast for me in glassfish)
with the persistence.xml having
<persistence-unit name="WebApplicationSecurityPU" transaction-type="RESOURCE_LOCAL">
the code
#PersistenceUnit
public EntityManagerFactory emf;
public EntityManager em;
public EntityManager getEm() {
emf = Persistence.createEntityManagerFactory("WebApplicationSecurityPU");
em = emf.createEntityManager();
return em;
}
public List fname (String id) {
String fname = null;
List persons = null;
try {
System.out.println("test");
em = this.getEm();
em.getTransaction().begin();
int m = em.createQuery("update Roleuser r set r.firstName = 'Jignesh H' where r.userID=9").executeUpdate();
em.getTransaction().commit();
} catch(Exception e) {
System.out.println("" + e);
}
finally {
if(em != null) {
em.close();
}
}
return persons;
}
Any improvements are welcome...(actually needed...)
(How to go about using #PersistenceContext)
Thanks
Pradyut
Your "normal" class is very likely not a managed component i.e. a class whose life cycle is managed by the container (like Servlets, Servlet Filters, JSP tag handlers, JSF Managed Beans, ...) and can't benefit from resource injection1.
So neither the UserTransaction nor the EntityManagerFactory are injected here, hence the NullPointerException.
Honestly, you should try to use a container managed EntityManager, this would make your code less messy. If you cannot get it injected, get it via a JNDI lookup. See the resource below.
1 Have a look at Web Tier to Go With Java EE 5: A Look at Resource Injection for a nice overview of what can be injected, and where.
Resources
How to use EntityManager API in web module
References
JPA 1.0 specification
Section 5.2 "Obtaining an Entity Manager"
Section 5.6 "Container-managed Persistence Contexts"