I would go with Approach 1. Assuming these truly exceptional conditions that the caller can handle.
I like exceptions because then there is a way to find out why something failed. Hiding all errors or just returning a boolean for success/fail is a bad practice.
The point is to try to hide as much internal stuff as possible to hide implementation details as well as stuff the other layers don't know about (or want to know about) but balance that with enough information if there is a problem.
Student s = dao.createStudent(...)
If the given parameters are invalid do you throw an exception? Maybe depends why they are invalid. Perhaps it's a good idea to make custom Exceptions for concepts such as "A Student with these parameters already exists". However, something like "parameter is invalid" might be a better choice to use one of the JDK built in exceptions like "IllegalArgumentException".
Furthermore, I would make all your custom Exceptions subclass a parent "DaoException" so client code that only cares about success/fail catch catch the parent, but something that wants more fine grained control can always catch subclasses.